summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author Winson <chiuwinson@google.com> 2020-07-23 11:58:05 -0700
committer Winson <chiuwinson@google.com> 2020-08-03 10:59:27 -0700
commit328aa43bff66ee65173c6b6cd27f441502c369ef (patch)
tree1420659948bca43bd422a5d79cdd733c3cf7a2da
parent8a6daa61035fd9c0d61e098ce40b7a52f8297b25 (diff)
Fix uninstall/disable with multi-user compressed stubs
Updates uninstall logic to ensure that only the user being acted on is affected. Previously, uninstalling from any user would disable the app for user 0. This updates it to only change the state of the stubbed package for the user that initiates the uninstall. This also changes the re-uncompression logic such that stubs are kept in their system-only state until any user has marked it installed and enabled. Also adds tests for all the uninstall scenarios for a stub system app. Some test cases are migrated from I6fdd3ed485db0d3f8135266010d6797d9ac37ea0. Only the corruption test isn't implemented, as this change doesn't verify the install/scan behavior at boot. Bug: 159204252 Test: atest com.android.server.pm.test.SystemStubMultiUserDisableUninstallTest Test: atest PackageManagerServiceHostTests Change-Id: I4ee2affc058d2bae363c537b3294d5d46a5051a0
-rw-r--r--services/core/java/com/android/server/pm/PackageManagerService.java50
-rw-r--r--services/core/java/com/android/server/pm/PackageManagerShellCommand.java10
-rw-r--r--services/tests/PackageManagerServiceTests/host/Android.bp33
-rw-r--r--services/tests/PackageManagerServiceTests/host/resources/PackageManagerDummyAppVersion3Invalid.apkbin2457 -> 0 bytes
-rw-r--r--services/tests/PackageManagerServiceTests/host/src/com/android/server/pm/test/HostUtils.kt56
-rw-r--r--services/tests/PackageManagerServiceTests/host/src/com/android/server/pm/test/InvalidNewSystemAppTest.kt16
-rw-r--r--services/tests/PackageManagerServiceTests/host/src/com/android/server/pm/test/OriginalPackageMigrationTest.kt17
-rw-r--r--services/tests/PackageManagerServiceTests/host/src/com/android/server/pm/test/SystemStubMultiUserDisableUninstallTest.kt650
-rw-r--r--services/tests/PackageManagerServiceTests/host/test-apps/Android.bp16
-rw-r--r--services/tests/PackageManagerServiceTests/host/test-apps/AndroidManifestOriginalOverride.xml4
-rw-r--r--services/tests/PackageManagerServiceTests/host/test-apps/AndroidManifestVersion1.xml4
-rw-r--r--services/tests/PackageManagerServiceTests/host/test-apps/AndroidManifestVersion2.xml4
-rw-r--r--services/tests/PackageManagerServiceTests/host/test-apps/AndroidManifestVersion3.xml4
-rw-r--r--services/tests/PackageManagerServiceTests/host/test-apps/AndroidManifestVersion4.xml4
-rw-r--r--tests/utils/hostutils/src/com/android/internal/util/test/SystemPreparer.java23
15 files changed, 820 insertions, 71 deletions
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 00ad661e39da..e47dd6cad9f2 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -18530,7 +18530,6 @@ public class PackageManagerService extends IPackageManager.Stub
// user handle installed state
int[] allUsers;
/** enabled state of the uninstalled application */
- final int origEnabledState;
synchronized (mLock) {
uninstalledPs = mSettings.mPackages.get(packageName);
if (uninstalledPs == null) {
@@ -18546,10 +18545,6 @@ public class PackageManagerService extends IPackageManager.Stub
}
disabledSystemPs = mSettings.getDisabledSystemPkgLPr(packageName);
- // Save the enabled state before we delete the package. When deleting a stub
- // application we always set the enabled state to 'disabled'.
- origEnabledState = uninstalledPs == null
- ? COMPONENT_ENABLED_STATE_DEFAULT : uninstalledPs.getEnabled(userId);
// Static shared libs can be declared by any package, so let us not
// allow removing a package if it provides a lib others depend on.
pkg = mPackages.get(packageName);
@@ -18628,20 +18623,32 @@ public class PackageManagerService extends IPackageManager.Stub
if (stubPkg != null && stubPkg.isStub()) {
final PackageSetting stubPs;
synchronized (mLock) {
- // restore the enabled state of the stub; the state is overwritten when
- // the stub is uninstalled
stubPs = mSettings.getPackageLPr(stubPkg.getPackageName());
- if (stubPs != null) {
- stubPs.setEnabled(origEnabledState, userId, "android");
- }
}
- if (origEnabledState == COMPONENT_ENABLED_STATE_DEFAULT
- || origEnabledState == COMPONENT_ENABLED_STATE_ENABLED) {
- if (DEBUG_COMPRESSION) {
- Slog.i(TAG, "Enabling system stub after removal; pkg: "
- + stubPkg.getPackageName());
+
+ if (stubPs != null) {
+ boolean enable = false;
+ for (int aUserId : allUsers) {
+ if (stubPs.getInstalled(aUserId)) {
+ int enabled = stubPs.getEnabled(aUserId);
+ if (enabled == COMPONENT_ENABLED_STATE_DEFAULT
+ || enabled == COMPONENT_ENABLED_STATE_ENABLED) {
+ enable = true;
+ break;
+ }
+ }
+ }
+
+ if (enable) {
+ if (DEBUG_COMPRESSION) {
+ Slog.i(TAG, "Enabling system stub after removal; pkg: "
+ + stubPkg.getPackageName());
+ }
+ enableCompressedPackage(stubPkg, stubPs);
+ } else if (DEBUG_COMPRESSION) {
+ Slog.i(TAG, "System stub disabled for all users, leaving uncompressed "
+ + "after removal; pkg: " + stubPkg.getPackageName());
}
- enableCompressedPackage(stubPkg, stubPs);
}
}
}
@@ -18985,8 +18992,15 @@ public class PackageManagerService extends IPackageManager.Stub
// and re-enable it afterward.
final PackageSetting stubPs = mSettings.mPackages.get(deletedPkg.getPackageName());
if (stubPs != null) {
- stubPs.setEnabled(
- COMPONENT_ENABLED_STATE_DISABLED, UserHandle.USER_SYSTEM, "android");
+ int userId = action.user == null
+ ? UserHandle.USER_ALL : action.user.getIdentifier();
+ if (userId == UserHandle.USER_ALL) {
+ for (int aUserId : allUserHandles) {
+ stubPs.setEnabled(COMPONENT_ENABLED_STATE_DISABLED, aUserId, "android");
+ }
+ } else if (userId >= UserHandle.USER_SYSTEM) {
+ stubPs.setEnabled(COMPONENT_ENABLED_STATE_DISABLED, userId, "android");
+ }
}
}
}
diff --git a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
index 668f375e2e9b..7aeec6d68d26 100644
--- a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
+++ b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
@@ -423,13 +423,15 @@ class PackageManagerShellCommand extends ShellCommand {
final List<ApplicationInfo> list;
if (packageName == null) {
final ParceledListSlice<ApplicationInfo> packages =
- mInterface.getInstalledApplications(
- PackageManager.MATCH_SYSTEM_ONLY, UserHandle.USER_SYSTEM);
+ mInterface.getInstalledApplications(PackageManager.MATCH_SYSTEM_ONLY
+ | PackageManager.MATCH_UNINSTALLED_PACKAGES,
+ UserHandle.USER_SYSTEM);
list = packages.getList();
} else {
list = new ArrayList<>(1);
- list.add(mInterface.getApplicationInfo(packageName,
- PackageManager.MATCH_SYSTEM_ONLY, UserHandle.USER_SYSTEM));
+ list.add(mInterface.getApplicationInfo(packageName, PackageManager.MATCH_SYSTEM_ONLY
+ | PackageManager.MATCH_UNINSTALLED_PACKAGES,
+ UserHandle.USER_SYSTEM));
}
for (ApplicationInfo info : list) {
if (info.isUpdatedSystemApp()) {
diff --git a/services/tests/PackageManagerServiceTests/host/Android.bp b/services/tests/PackageManagerServiceTests/host/Android.bp
index 41dfade1a09a..cffcdd8f94bd 100644
--- a/services/tests/PackageManagerServiceTests/host/Android.bp
+++ b/services/tests/PackageManagerServiceTests/host/Android.bp
@@ -25,17 +25,28 @@ java_test_host {
],
test_suites: ["general-tests"],
java_resources: [
- ":PackageManagerDummyAppVersion1",
- ":PackageManagerDummyAppVersion2",
- ":PackageManagerDummyAppVersion3",
- ":PackageManagerDummyAppVersion4",
- ":PackageManagerDummyAppOriginalOverride",
- ":PackageManagerServiceHostTestsResources",
- ]
+ ":PackageManagerTestAppStub",
+ ":PackageManagerTestAppVersion1",
+ ":PackageManagerTestAppVersion2",
+ ":PackageManagerTestAppVersion3",
+ ":PackageManagerTestAppVersion3Invalid",
+ ":PackageManagerTestAppVersion4",
+ ":PackageManagerTestAppOriginalOverride",
+ ],
}
-filegroup {
- name: "PackageManagerServiceHostTestsResources",
- srcs: [ "resources/*" ],
- path: "resources/"
+genrule {
+ name: "PackageManagerTestAppVersion3Invalid",
+ tools: [
+ "soong_zip",
+ "zipalign",
+ ],
+ srcs: [
+ ":PackageManagerTestAppVersion3",
+ ],
+ out: ["PackageManagerTestAppVersion3Invalid.apk"],
+ cmd: "mkdir -p $(genDir)/apk && unzip $(in) -d $(genDir)/apk" +
+ " && truncate -s 800 $(genDir)/apk/META-INF/CERT.RSA" +
+ " && $(location soong_zip) -o $(genDir)/temp.apk -L 0 -C $(genDir)/apk -D $(genDir)/apk" +
+ " && $(location zipalign) -f 4 $(genDir)/temp.apk $(out)",
}
diff --git a/services/tests/PackageManagerServiceTests/host/resources/PackageManagerDummyAppVersion3Invalid.apk b/services/tests/PackageManagerServiceTests/host/resources/PackageManagerDummyAppVersion3Invalid.apk
deleted file mode 100644
index 127886cf8e9e..000000000000
--- a/services/tests/PackageManagerServiceTests/host/resources/PackageManagerDummyAppVersion3Invalid.apk
+++ /dev/null
Binary files differ
diff --git a/services/tests/PackageManagerServiceTests/host/src/com/android/server/pm/test/HostUtils.kt b/services/tests/PackageManagerServiceTests/host/src/com/android/server/pm/test/HostUtils.kt
index 234fcf19062d..8dfefaf9750f 100644
--- a/services/tests/PackageManagerServiceTests/host/src/com/android/server/pm/test/HostUtils.kt
+++ b/services/tests/PackageManagerServiceTests/host/src/com/android/server/pm/test/HostUtils.kt
@@ -21,8 +21,9 @@ import com.android.tradefed.device.ITestDevice
import java.io.File
import java.io.FileOutputStream
-internal fun SystemPreparer.pushApk(file: String, partition: Partition) =
- pushResourceFile(file, HostUtils.makePathForApk(file, partition).toString())
+internal fun SystemPreparer.pushApk(javaResourceName: String, partition: Partition) =
+ pushResourceFile(javaResourceName, HostUtils.makePathForApk(javaResourceName, partition)
+ .toString())
internal fun SystemPreparer.deleteApkFolders(
partition: Partition,
@@ -58,4 +59,55 @@ internal object HostUtils {
}
return file
}
+
+ /**
+ * dumpsys package and therefore device.getAppPackageInfo doesn't work immediately after reboot,
+ * so the following methods parse the package dump directly to see if the path matches.
+ */
+ fun getCodePaths(device: ITestDevice, pkgName: String) =
+ device.executeShellCommand("pm dump $pkgName")
+ .lineSequence()
+ .map(String::trim)
+ .filter { it.startsWith("codePath=") }
+ .map { it.removePrefix("codePath=") }
+ .toList()
+
+ private fun userIdLineSequence(device: ITestDevice, pkgName: String) =
+ device.executeShellCommand("pm dump $pkgName")
+ .lineSequence()
+ .dropWhile { !it.startsWith("Packages:") }
+ .takeWhile {
+ !it.startsWith("Hidden system packages:") &&
+ !it.startsWith("Queries:")
+ }
+ .map(String::trim)
+ .filter { it.startsWith("User ") }
+
+ fun getUserIdToPkgEnabledState(device: ITestDevice, pkgName: String) =
+ userIdLineSequence(device, pkgName).associate {
+ val userId = it.removePrefix("User ")
+ .takeWhile(Char::isDigit)
+ .toInt()
+ val enabled = it.substringAfter("enabled=")
+ .takeWhile(Char::isDigit)
+ .toInt()
+ .let {
+ when (it) {
+ 0, 1 -> true
+ else -> false
+ }
+ }
+ userId to enabled
+ }
+
+ fun getUserIdToPkgInstalledState(device: ITestDevice, pkgName: String) =
+ userIdLineSequence(device, pkgName).associate {
+ val userId = it.removePrefix("User ")
+ .takeWhile(Char::isDigit)
+ .toInt()
+ val installed = it.substringAfter("installed=")
+ .takeWhile { !it.isWhitespace() }
+ .toBoolean()
+ userId to installed
+ }
}
diff --git a/services/tests/PackageManagerServiceTests/host/src/com/android/server/pm/test/InvalidNewSystemAppTest.kt b/services/tests/PackageManagerServiceTests/host/src/com/android/server/pm/test/InvalidNewSystemAppTest.kt
index 39b40d8d3195..b7d135991ccd 100644
--- a/services/tests/PackageManagerServiceTests/host/src/com/android/server/pm/test/InvalidNewSystemAppTest.kt
+++ b/services/tests/PackageManagerServiceTests/host/src/com/android/server/pm/test/InvalidNewSystemAppTest.kt
@@ -33,11 +33,11 @@ import org.junit.runner.RunWith
class InvalidNewSystemAppTest : BaseHostJUnit4Test() {
companion object {
- private const val TEST_PKG_NAME = "com.android.server.pm.test.dummy_app"
- private const val VERSION_ONE = "PackageManagerDummyAppVersion1.apk"
- private const val VERSION_TWO = "PackageManagerDummyAppVersion2.apk"
- private const val VERSION_THREE_INVALID = "PackageManagerDummyAppVersion3Invalid.apk"
- private const val VERSION_FOUR = "PackageManagerDummyAppVersion4.apk"
+ private const val TEST_PKG_NAME = "com.android.server.pm.test.test_app"
+ private const val VERSION_ONE = "PackageManagerTestAppVersion1.apk"
+ private const val VERSION_TWO = "PackageManagerTestAppVersion2.apk"
+ private const val VERSION_THREE_INVALID = "PackageManagerTestAppVersion3Invalid.apk"
+ private const val VERSION_FOUR = "PackageManagerTestAppVersion4.apk"
@get:ClassRule
val deviceRebootRule = SystemPreparer.TestRuleDelegate(true)
@@ -49,14 +49,14 @@ class InvalidNewSystemAppTest : BaseHostJUnit4Test() {
@get:Rule
val rules = RuleChain.outerRule(tempFolder).around(preparer)!!
- private val filePath = HostUtils.makePathForApk("PackageManagerDummyApp.apk", Partition.PRODUCT)
+ private val filePath = HostUtils.makePathForApk("PackageManagerTestApp.apk", Partition.PRODUCT)
@Before
@After
fun removeApk() {
device.uninstallPackage(TEST_PKG_NAME)
- device.deleteFile(filePath.parent.toString())
- device.reboot()
+ preparer.deleteFile(filePath.parent.toString())
+ .reboot()
}
@Test
diff --git a/services/tests/PackageManagerServiceTests/host/src/com/android/server/pm/test/OriginalPackageMigrationTest.kt b/services/tests/PackageManagerServiceTests/host/src/com/android/server/pm/test/OriginalPackageMigrationTest.kt
index fb0348c3146b..4ae3ca5f7263 100644
--- a/services/tests/PackageManagerServiceTests/host/src/com/android/server/pm/test/OriginalPackageMigrationTest.kt
+++ b/services/tests/PackageManagerServiceTests/host/src/com/android/server/pm/test/OriginalPackageMigrationTest.kt
@@ -33,11 +33,11 @@ import org.junit.runner.RunWith
class OriginalPackageMigrationTest : BaseHostJUnit4Test() {
companion object {
- private const val TEST_PKG_NAME = "com.android.server.pm.test.dummy_app"
- private const val VERSION_ONE = "PackageManagerDummyAppVersion1.apk"
- private const val VERSION_TWO = "PackageManagerDummyAppVersion2.apk"
- private const val VERSION_THREE = "PackageManagerDummyAppVersion3.apk"
- private const val NEW_PKG = "PackageManagerDummyAppOriginalOverride.apk"
+ private const val TEST_PKG_NAME = "com.android.server.pm.test.test_app"
+ private const val VERSION_ONE = "PackageManagerTestAppVersion1.apk"
+ private const val VERSION_TWO = "PackageManagerTestAppVersion2.apk"
+ private const val VERSION_THREE = "PackageManagerTestAppVersion3.apk"
+ private const val NEW_PKG = "PackageManagerTestAppOriginalOverride.apk"
@get:ClassRule
val deviceRebootRule = SystemPreparer.TestRuleDelegate(true)
@@ -55,6 +55,7 @@ class OriginalPackageMigrationTest : BaseHostJUnit4Test() {
fun deleteApkFolders() {
preparer.deleteApkFolders(Partition.SYSTEM, VERSION_ONE, VERSION_TWO, VERSION_THREE,
NEW_PKG)
+ .reboot()
}
@Test
@@ -99,9 +100,7 @@ class OriginalPackageMigrationTest : BaseHostJUnit4Test() {
}
private fun assertCodePath(apk: String) {
- // dumpsys package and therefore device.getAppPackageInfo doesn't work here for some reason,
- // so parse the package dump directly to see if the path matches.
- assertThat(device.executeShellCommand("pm dump $TEST_PKG_NAME"))
- .contains(HostUtils.makePathForApk(apk, Partition.SYSTEM).parent.toString())
+ assertThat(HostUtils.getCodePaths(device, TEST_PKG_NAME))
+ .containsExactly(HostUtils.makePathForApk(apk, Partition.SYSTEM).parent.toString())
}
}
diff --git a/services/tests/PackageManagerServiceTests/host/src/com/android/server/pm/test/SystemStubMultiUserDisableUninstallTest.kt b/services/tests/PackageManagerServiceTests/host/src/com/android/server/pm/test/SystemStubMultiUserDisableUninstallTest.kt
new file mode 100644
index 000000000000..bc478b0e2c90
--- /dev/null
+++ b/services/tests/PackageManagerServiceTests/host/src/com/android/server/pm/test/SystemStubMultiUserDisableUninstallTest.kt
@@ -0,0 +1,650 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.pm.test
+
+import com.android.internal.util.test.SystemPreparer
+import com.android.tradefed.device.ITestDevice
+import com.android.tradefed.device.UserInfo
+import com.android.tradefed.testtype.DeviceJUnit4ClassRunner
+import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test
+import com.google.common.truth.Truth.assertThat
+import org.junit.AfterClass
+import org.junit.Before
+import org.junit.ClassRule
+import org.junit.Rule
+import org.junit.Test
+import org.junit.rules.RuleChain
+import org.junit.rules.TemporaryFolder
+import org.junit.runner.RunWith
+import java.io.File
+import java.util.zip.GZIPOutputStream
+
+@RunWith(DeviceJUnit4ClassRunner::class)
+class SystemStubMultiUserDisableUninstallTest : BaseHostJUnit4Test() {
+
+ companion object {
+ private const val TEST_PKG_NAME = "com.android.server.pm.test.test_app"
+ private const val VERSION_STUB = "PackageManagerTestAppStub.apk"
+ private const val VERSION_ONE = "PackageManagerTestAppVersion1.apk"
+
+ /**
+ * How many total users on device to test, including primary. This will clean up any
+ * users created specifically for this test.
+ */
+ private const val USER_COUNT = 3
+
+ /**
+ * Whether to manually reset state at each test method without rebooting
+ * for faster iterative development.
+ */
+ private const val DEBUG_NO_REBOOT = false
+
+ @get:ClassRule
+ val deviceRebootRule = SystemPreparer.TestRuleDelegate(true)
+
+ private val parentClassName = SystemStubMultiUserDisableUninstallTest::class.java.simpleName
+
+ private val deviceCompressedFile =
+ HostUtils.makePathForApk("$parentClassName.apk", Partition.PRODUCT).parent
+ .resolve("$parentClassName.apk.gz")
+
+ private val stubFile =
+ HostUtils.makePathForApk("$parentClassName-Stub.apk", Partition.PRODUCT)
+
+ private val secondaryUsers = mutableListOf<Int>()
+ private val usersToRemove = mutableListOf<Int>()
+ private var savedDevice: ITestDevice? = null
+ private var savedPreparer: SystemPreparer? = null
+
+ private fun setUpUsers(device: ITestDevice) {
+ if (this.savedDevice != null) return
+ this.savedDevice = device
+ secondaryUsers.clear()
+ secondaryUsers += device.userInfos.values.map(UserInfo::userId).filterNot { it == 0 }
+ while (secondaryUsers.size < USER_COUNT) {
+ secondaryUsers += device.createUser(parentClassName + secondaryUsers.size)
+ .also { usersToRemove += it }
+ }
+ }
+
+ @JvmStatic
+ @AfterClass
+ fun cleanUp() {
+ savedDevice ?: return
+
+ usersToRemove.forEach {
+ savedDevice?.removeUser(it)
+ }
+
+ savedDevice?.uninstallPackage(TEST_PKG_NAME)
+ savedDevice?.deleteFile(stubFile.parent.toString())
+ savedDevice?.deleteFile(deviceCompressedFile.parent.toString())
+ savedDevice?.reboot()
+ savedDevice = null
+
+ if (DEBUG_NO_REBOOT) {
+ savedPreparer?.after()
+ savedPreparer = null
+ }
+ }
+ }
+
+ private val tempFolder = TemporaryFolder()
+ private val preparer: SystemPreparer = SystemPreparer(tempFolder,
+ SystemPreparer.RebootStrategy.START_STOP, deviceRebootRule) { this.device }
+
+ @get:Rule
+ val rules = RuleChain.outerRule(tempFolder).let {
+ if (DEBUG_NO_REBOOT) {
+ it!!
+ } else {
+ it.around(preparer)!!
+ }
+ }
+
+ private var hostCompressedFile: File? = null
+
+ private val previousCodePaths = mutableListOf<String>()
+
+ @Before
+ fun ensureUserAndCompressStubAndInstall() {
+ setUpUsers(device)
+
+ val initialized = hostCompressedFile != null
+ if (!initialized) {
+ hostCompressedFile = tempFolder.newFile()
+ hostCompressedFile!!.outputStream().use {
+ javaClass.classLoader
+ .getResource(VERSION_ONE)!!
+ .openStream()
+ .use { input ->
+ GZIPOutputStream(it).use { output ->
+ input.copyTo(output)
+ }
+ }
+ }
+ }
+
+ device.uninstallPackage(TEST_PKG_NAME)
+
+ if (!initialized || !DEBUG_NO_REBOOT) {
+ savedPreparer = preparer
+ preparer.pushResourceFile(VERSION_STUB, stubFile.toString())
+ .pushFile(hostCompressedFile, deviceCompressedFile.toString())
+ .reboot()
+ }
+
+ // This test forces the state to installed/enabled for all users,
+ // since it only tests the uninstall/disable side.
+ installExisting(User.PRIMARY)
+ installExisting(User.SECONDARY)
+
+ ensureEnabled()
+
+ // Ensure data app isn't re-installed multiple times by comparing against the original path
+ val codePath = HostUtils.getCodePaths(device, TEST_PKG_NAME).first()
+ assertThat(codePath).contains("/data/app")
+ assertThat(codePath).contains(TEST_PKG_NAME)
+
+ previousCodePaths.clear()
+ previousCodePaths += codePath
+
+ assertState(
+ primaryInstalled = true, primaryEnabled = true,
+ secondaryInstalled = true, secondaryEnabled = true,
+ codePaths = listOf(CodePath.SAME, CodePath.SYSTEM)
+ )
+ }
+
+ @Test
+ fun disablePrimaryFirstAndUninstall() {
+ toggleEnabled(false, User.PRIMARY)
+
+ assertState(
+ primaryInstalled = true, primaryEnabled = false,
+ secondaryInstalled = true, secondaryEnabled = true,
+ codePaths = listOf(CodePath.SAME, CodePath.SYSTEM)
+ )
+
+ toggleEnabled(false, User.SECONDARY)
+
+ assertState(
+ primaryInstalled = true, primaryEnabled = false,
+ secondaryInstalled = true, secondaryEnabled = false,
+ codePaths = listOf(CodePath.SAME, CodePath.SYSTEM)
+ )
+
+ device.uninstallPackage(TEST_PKG_NAME)
+
+ assertState(
+ primaryInstalled = true, primaryEnabled = false,
+ secondaryInstalled = true, secondaryEnabled = false,
+ codePaths = listOf(CodePath.SYSTEM)
+ )
+ }
+
+ @Test
+ fun disableSecondaryFirstAndUninstall() {
+ toggleEnabled(false, User.SECONDARY)
+
+ assertState(
+ primaryInstalled = true, primaryEnabled = true,
+ secondaryInstalled = true, secondaryEnabled = false,
+ codePaths = listOf(CodePath.SAME, CodePath.SYSTEM)
+ )
+
+ toggleEnabled(false, User.PRIMARY)
+
+ assertState(
+ primaryInstalled = true, primaryEnabled = false,
+ secondaryInstalled = true, secondaryEnabled = false,
+ codePaths = listOf(CodePath.SAME, CodePath.SYSTEM)
+ )
+
+ device.uninstallPackage(TEST_PKG_NAME)
+
+ assertState(
+ primaryInstalled = true, primaryEnabled = false,
+ secondaryInstalled = true, secondaryEnabled = false,
+ codePaths = listOf(CodePath.SYSTEM)
+ )
+ }
+
+ @Test
+ fun disabledUninstalledEnablePrimaryFirst() {
+ toggleEnabled(false, User.PRIMARY)
+ toggleEnabled(false, User.SECONDARY)
+ device.uninstallPackage(TEST_PKG_NAME)
+
+ toggleEnabled(true, User.PRIMARY)
+
+ assertState(
+ primaryInstalled = true, primaryEnabled = true,
+ secondaryInstalled = true, secondaryEnabled = false,
+ codePaths = listOf(CodePath.DIFFERENT, CodePath.SYSTEM)
+ )
+
+ toggleEnabled(true, User.SECONDARY)
+
+ assertState(
+ primaryInstalled = true, primaryEnabled = true,
+ secondaryInstalled = true, secondaryEnabled = true,
+ codePaths = listOf(CodePath.SAME, CodePath.SYSTEM)
+ )
+ }
+
+ @Test
+ fun disabledUninstalledEnableSecondaryFirst() {
+ toggleEnabled(false, User.PRIMARY)
+ toggleEnabled(false, User.SECONDARY)
+ device.uninstallPackage(TEST_PKG_NAME)
+
+ toggleEnabled(true, User.SECONDARY)
+
+ assertState(
+ primaryInstalled = true, primaryEnabled = false,
+ secondaryInstalled = true, secondaryEnabled = true,
+ codePaths = listOf(CodePath.DIFFERENT, CodePath.SYSTEM)
+ )
+
+ toggleEnabled(true, User.PRIMARY)
+
+ assertState(
+ primaryInstalled = true, primaryEnabled = true,
+ secondaryInstalled = true, secondaryEnabled = true,
+ codePaths = listOf(CodePath.SAME, CodePath.SYSTEM)
+ )
+ }
+
+ @Test
+ fun uninstallPrimaryFirstByUserAndInstallExistingPrimaryFirst() {
+ uninstall(User.PRIMARY)
+
+ assertState(
+ primaryInstalled = false, primaryEnabled = true,
+ secondaryInstalled = true, secondaryEnabled = true,
+ codePaths = listOf(CodePath.DIFFERENT, CodePath.SYSTEM)
+ )
+
+ uninstall(User.SECONDARY)
+
+ assertState(
+ primaryInstalled = false, primaryEnabled = true,
+ secondaryInstalled = false, secondaryEnabled = true,
+ codePaths = listOf(CodePath.DIFFERENT, CodePath.SYSTEM)
+ )
+
+ installExisting(User.PRIMARY)
+
+ assertState(
+ primaryInstalled = true, primaryEnabled = true,
+ secondaryInstalled = false, secondaryEnabled = true,
+ codePaths = listOf(CodePath.SAME, CodePath.SYSTEM)
+ )
+
+ installExisting(User.SECONDARY)
+
+ assertState(
+ primaryInstalled = true, primaryEnabled = true,
+ secondaryInstalled = true, secondaryEnabled = true,
+ codePaths = listOf(CodePath.SAME, CodePath.SYSTEM)
+ )
+ }
+
+ @Test
+ fun uninstallSecondaryFirstByUserAndInstallExistingSecondaryFirst() {
+ uninstall(User.PRIMARY)
+
+ assertState(
+ primaryInstalled = false, primaryEnabled = true,
+ secondaryInstalled = true, secondaryEnabled = true,
+ codePaths = listOf(CodePath.DIFFERENT, CodePath.SYSTEM)
+ )
+
+ uninstall(User.SECONDARY)
+
+ assertState(
+ primaryInstalled = false, primaryEnabled = true,
+ secondaryInstalled = false, secondaryEnabled = true,
+ codePaths = listOf(CodePath.DIFFERENT, CodePath.SYSTEM)
+ )
+
+ installExisting(User.SECONDARY)
+
+ assertState(
+ primaryInstalled = false, primaryEnabled = true,
+ secondaryInstalled = true, secondaryEnabled = true,
+ codePaths = listOf(CodePath.SAME, CodePath.SYSTEM)
+ )
+
+ installExisting(User.PRIMARY)
+
+ assertState(
+ primaryInstalled = true, primaryEnabled = true,
+ secondaryInstalled = true, secondaryEnabled = true,
+ codePaths = listOf(CodePath.SAME, CodePath.SYSTEM)
+ )
+ }
+
+ @Test
+ fun uninstallUpdatesAndEnablePrimaryFirst() {
+ device.executeShellCommand("pm uninstall-system-updates $TEST_PKG_NAME")
+
+ // Uninstall-system-updates always disables system user 0
+ // TODO: Is this intentional? There is no user argument for this command.
+ assertState(
+ primaryInstalled = true, primaryEnabled = false,
+ secondaryInstalled = true, secondaryEnabled = true,
+ // If any user is enabled when uninstalling updates, /data is re-uncompressed
+ codePaths = listOf(CodePath.DIFFERENT, CodePath.SYSTEM)
+ )
+
+ toggleEnabled(true, User.PRIMARY)
+
+ assertState(
+ primaryInstalled = true, primaryEnabled = true,
+ secondaryInstalled = true, secondaryEnabled = true,
+ codePaths = listOf(CodePath.SAME, CodePath.SYSTEM)
+ )
+
+ // Test enabling secondary to ensure path does not change, even though it's already enabled
+ toggleEnabled(true, User.SECONDARY)
+
+ assertState(
+ primaryInstalled = true, primaryEnabled = true,
+ secondaryInstalled = true, secondaryEnabled = true,
+ codePaths = listOf(CodePath.SAME, CodePath.SYSTEM)
+ )
+ }
+
+ @Test
+ fun uninstallUpdatesAndEnableSecondaryFirst() {
+ device.executeShellCommand("pm uninstall-system-updates $TEST_PKG_NAME")
+
+ // Uninstall-system-updates always disables system user 0
+ assertState(
+ primaryInstalled = true, primaryEnabled = false,
+ secondaryInstalled = true, secondaryEnabled = true,
+ // If any user is enabled when uninstalling updates, /data is re-uncompressed
+ codePaths = listOf(CodePath.DIFFERENT, CodePath.SYSTEM)
+ )
+
+ toggleEnabled(true, User.SECONDARY)
+
+ assertState(
+ primaryInstalled = true, primaryEnabled = false,
+ secondaryInstalled = true, secondaryEnabled = true,
+ codePaths = listOf(CodePath.SAME, CodePath.SYSTEM)
+ )
+
+ toggleEnabled(true, User.PRIMARY)
+
+ assertState(
+ primaryInstalled = true, primaryEnabled = true,
+ secondaryInstalled = true, secondaryEnabled = true,
+ codePaths = listOf(CodePath.SAME, CodePath.SYSTEM)
+ )
+ }
+
+ @Test
+ fun disabledUninstallUpdatesAndEnablePrimaryFirst() {
+ toggleEnabled(false, User.PRIMARY)
+ toggleEnabled(false, User.SECONDARY)
+
+ device.executeShellCommand("pm uninstall-system-updates $TEST_PKG_NAME")
+
+ assertState(
+ primaryInstalled = true, primaryEnabled = false,
+ secondaryInstalled = true, secondaryEnabled = false,
+ codePaths = listOf(CodePath.SYSTEM)
+ )
+
+ toggleEnabled(true, User.PRIMARY)
+
+ assertState(
+ primaryInstalled = true, primaryEnabled = true,
+ secondaryInstalled = true, secondaryEnabled = false,
+ codePaths = listOf(CodePath.DIFFERENT, CodePath.SYSTEM)
+ )
+
+ toggleEnabled(true, User.SECONDARY)
+
+ assertState(
+ primaryInstalled = true, primaryEnabled = true,
+ secondaryInstalled = true, secondaryEnabled = true,
+ codePaths = listOf(CodePath.SAME, CodePath.SYSTEM)
+ )
+ }
+
+ @Test
+ fun disabledUninstallUpdatesAndEnableSecondaryFirst() {
+ toggleEnabled(false, User.PRIMARY)
+ toggleEnabled(false, User.SECONDARY)
+
+ device.executeShellCommand("pm uninstall-system-updates $TEST_PKG_NAME")
+
+ assertState(
+ primaryInstalled = true, primaryEnabled = false,
+ secondaryInstalled = true, secondaryEnabled = false,
+ codePaths = listOf(CodePath.SYSTEM)
+ )
+
+ toggleEnabled(true, User.SECONDARY)
+
+ assertState(
+ primaryInstalled = true, primaryEnabled = false,
+ secondaryInstalled = true, secondaryEnabled = true,
+ codePaths = listOf(CodePath.DIFFERENT, CodePath.SYSTEM)
+ )
+
+ toggleEnabled(true, User.PRIMARY)
+
+ assertState(
+ primaryInstalled = true, primaryEnabled = true,
+ secondaryInstalled = true, secondaryEnabled = true,
+ codePaths = listOf(CodePath.SAME, CodePath.SYSTEM)
+ )
+ }
+
+ @Test
+ fun uninstalledUninstallUpdatesAndEnablePrimaryFirst() {
+ uninstall(User.PRIMARY)
+ uninstall(User.SECONDARY)
+
+ device.executeShellCommand("pm uninstall-system-updates $TEST_PKG_NAME")
+
+ // Uninstall-system-updates always disables system user 0
+ assertState(
+ primaryInstalled = false, primaryEnabled = false,
+ secondaryInstalled = false, secondaryEnabled = true,
+ codePaths = listOf(CodePath.SYSTEM)
+ )
+
+ toggleEnabled(true, User.PRIMARY)
+
+ assertState(
+ primaryInstalled = false, primaryEnabled = true,
+ secondaryInstalled = false, secondaryEnabled = true,
+ codePaths = listOf(CodePath.DIFFERENT, CodePath.SYSTEM)
+ )
+
+ toggleEnabled(true, User.SECONDARY)
+
+ assertState(
+ primaryInstalled = false, primaryEnabled = true,
+ secondaryInstalled = false, secondaryEnabled = true,
+ codePaths = listOf(CodePath.SAME, CodePath.SYSTEM)
+ )
+ }
+
+ @Test
+ fun uninstalledUninstallUpdatesAndEnableSecondaryFirst() {
+ uninstall(User.PRIMARY)
+ uninstall(User.SECONDARY)
+
+ device.executeShellCommand("pm uninstall-system-updates $TEST_PKG_NAME")
+
+ // Uninstall-system-updates always disables system user 0
+ assertState(
+ primaryInstalled = false, primaryEnabled = false,
+ secondaryInstalled = false, secondaryEnabled = true,
+ codePaths = listOf(CodePath.SYSTEM)
+ )
+
+ toggleEnabled(true, User.SECONDARY)
+
+ assertState(
+ primaryInstalled = false, primaryEnabled = false,
+ secondaryInstalled = false, secondaryEnabled = true,
+ codePaths = listOf(CodePath.DIFFERENT, CodePath.SYSTEM)
+ )
+
+ toggleEnabled(true, User.PRIMARY)
+
+ assertState(
+ primaryInstalled = false, primaryEnabled = true,
+ secondaryInstalled = false, secondaryEnabled = true,
+ codePaths = listOf(CodePath.SAME, CodePath.SYSTEM)
+ )
+ }
+
+ private fun ensureEnabled() {
+ toggleEnabled(true, User.PRIMARY)
+ toggleEnabled(true, User.SECONDARY)
+
+ assertThat(HostUtils.getUserIdToPkgEnabledState(device, TEST_PKG_NAME).all { it.value })
+ .isTrue()
+ }
+
+ private fun toggleEnabled(enabled: Boolean, user: User, pkgName: String = TEST_PKG_NAME) {
+ val command = if (enabled) "enable" else "disable"
+ @Suppress("UNUSED_VARIABLE") val exhaust: Any = when (user) {
+ User.PRIMARY -> {
+ device.executeShellCommand("pm $command --user 0 $pkgName")
+ }
+ User.SECONDARY -> {
+ secondaryUsers.forEach {
+ device.executeShellCommand("pm $command --user $it $pkgName")
+ }
+ }
+ }
+ }
+
+ private fun uninstall(user: User, pkgName: String = TEST_PKG_NAME) {
+ @Suppress("UNUSED_VARIABLE") val exhaust: Any = when (user) {
+ User.PRIMARY -> {
+ device.executeShellCommand("pm uninstall --user 0 $pkgName")
+ }
+ User.SECONDARY -> {
+ secondaryUsers.forEach {
+ device.executeShellCommand("pm uninstall --user $it $pkgName")
+ }
+ }
+ }
+ }
+
+ private fun installExisting(user: User, pkgName: String = TEST_PKG_NAME) {
+ @Suppress("UNUSED_VARIABLE") val exhaust: Any = when (user) {
+ User.PRIMARY -> {
+ device.executeShellCommand("pm install-existing --user 0 $pkgName")
+ }
+ User.SECONDARY -> {
+ secondaryUsers.forEach {
+ device.executeShellCommand("pm install-existing --user $it $pkgName")
+ }
+ }
+ }
+ }
+
+ private fun assertState(
+ primaryInstalled: Boolean,
+ primaryEnabled: Boolean,
+ secondaryInstalled: Boolean,
+ secondaryEnabled: Boolean,
+ codePaths: List<CodePath>
+ ) {
+ HostUtils.getUserIdToPkgInstalledState(device, TEST_PKG_NAME)
+ .forEach { (userId, installed) ->
+ if (userId == 0) {
+ assertThat(installed).isEqualTo(primaryInstalled)
+ } else {
+ assertThat(installed).isEqualTo(secondaryInstalled)
+ }
+ }
+
+ HostUtils.getUserIdToPkgEnabledState(device, TEST_PKG_NAME)
+ .forEach { (userId, enabled) ->
+ if (userId == 0) {
+ assertThat(enabled).isEqualTo(primaryEnabled)
+ } else {
+ assertThat(enabled).isEqualTo(secondaryEnabled)
+ }
+ }
+
+ assertCodePaths(codePaths.first(), codePaths.getOrNull(1))
+ }
+
+ private fun assertCodePaths(firstCodePath: CodePath, secondCodePath: CodePath? = null) {
+ val codePaths = HostUtils.getCodePaths(device, TEST_PKG_NAME)
+ assertThat(codePaths).hasSize(listOfNotNull(firstCodePath, secondCodePath).size)
+
+ when (firstCodePath) {
+ CodePath.SAME -> {
+ assertThat(codePaths[0]).contains("/data/app")
+ assertThat(codePaths[0]).contains(TEST_PKG_NAME)
+ assertThat(codePaths[0]).isEqualTo(previousCodePaths.last())
+ }
+ CodePath.DIFFERENT -> {
+ assertThat(codePaths[0]).contains("/data/app")
+ assertThat(codePaths[0]).contains(TEST_PKG_NAME)
+ assertThat(previousCodePaths).doesNotContain(codePaths[0])
+ previousCodePaths.add(codePaths[0])
+ }
+ CodePath.SYSTEM -> assertThat(codePaths[0]).isEqualTo(stubFile.parent.toString())
+ }
+
+ when (secondCodePath) {
+ CodePath.SAME, CodePath.DIFFERENT ->
+ throw AssertionError("secondDataPath cannot be a data path")
+ CodePath.SYSTEM -> assertThat(codePaths[1]).isEqualTo(stubFile.parent.toString())
+ }
+ }
+
+ enum class User {
+ /** The primary system user 0 */
+ PRIMARY,
+
+ /**
+ * All other users on the device that are not 0. This is split into an enum so that all
+ * methods that handle secondary act on all non-system users. Some behaviors only occur
+ * if a package state is marked for all non-primary users on the device, which can be
+ * more than just 1.
+ */
+ SECONDARY
+ }
+
+ enum class CodePath {
+ /** The data code path hasn't changed */
+ SAME,
+
+ /** New data code path */
+ DIFFERENT,
+
+ /** The static system code path */
+ SYSTEM
+ }
+}
diff --git a/services/tests/PackageManagerServiceTests/host/test-apps/Android.bp b/services/tests/PackageManagerServiceTests/host/test-apps/Android.bp
index c9b29275a731..4a3076e4736a 100644
--- a/services/tests/PackageManagerServiceTests/host/test-apps/Android.bp
+++ b/services/tests/PackageManagerServiceTests/host/test-apps/Android.bp
@@ -13,26 +13,32 @@
// limitations under the License.
android_test_helper_app {
- name: "PackageManagerDummyAppVersion1",
+ name: "PackageManagerTestAppStub",
+ manifest: "AndroidManifestVersion1.xml",
+ srcs: []
+}
+
+android_test_helper_app {
+ name: "PackageManagerTestAppVersion1",
manifest: "AndroidManifestVersion1.xml"
}
android_test_helper_app {
- name: "PackageManagerDummyAppVersion2",
+ name: "PackageManagerTestAppVersion2",
manifest: "AndroidManifestVersion2.xml"
}
android_test_helper_app {
- name: "PackageManagerDummyAppVersion3",
+ name: "PackageManagerTestAppVersion3",
manifest: "AndroidManifestVersion3.xml"
}
android_test_helper_app {
- name: "PackageManagerDummyAppVersion4",
+ name: "PackageManagerTestAppVersion4",
manifest: "AndroidManifestVersion4.xml"
}
android_test_helper_app {
- name: "PackageManagerDummyAppOriginalOverride",
+ name: "PackageManagerTestAppOriginalOverride",
manifest: "AndroidManifestOriginalOverride.xml"
}
diff --git a/services/tests/PackageManagerServiceTests/host/test-apps/AndroidManifestOriginalOverride.xml b/services/tests/PackageManagerServiceTests/host/test-apps/AndroidManifestOriginalOverride.xml
index f16e1bc8a927..cba580e44065 100644
--- a/services/tests/PackageManagerServiceTests/host/test-apps/AndroidManifestOriginalOverride.xml
+++ b/services/tests/PackageManagerServiceTests/host/test-apps/AndroidManifestOriginalOverride.xml
@@ -16,10 +16,10 @@
-->
<manifest
xmlns:android="http://schemas.android.com/apk/res/android"
- package="com.android.server.pm.test.dummy_app.override"
+ package="com.android.server.pm.test.test_app.override"
android:versionCode="2"
>
- <original-package android:name="com.android.server.pm.test.dummy_app"/>
+ <original-package android:name="com.android.server.pm.test.test_app"/>
</manifest>
diff --git a/services/tests/PackageManagerServiceTests/host/test-apps/AndroidManifestVersion1.xml b/services/tests/PackageManagerServiceTests/host/test-apps/AndroidManifestVersion1.xml
index b492a31349fc..efc7372a177c 100644
--- a/services/tests/PackageManagerServiceTests/host/test-apps/AndroidManifestVersion1.xml
+++ b/services/tests/PackageManagerServiceTests/host/test-apps/AndroidManifestVersion1.xml
@@ -16,12 +16,12 @@
-->
<manifest
xmlns:android="http://schemas.android.com/apk/res/android"
- package="com.android.server.pm.test.dummy_app"
+ package="com.android.server.pm.test.test_app"
android:versionCode="1"
>
<permission
- android:name="com.android.server.pm.test.dummy_app.TEST_PERMISSION"
+ android:name="com.android.server.pm.test.test_app.TEST_PERMISSION"
android:protectionLevel="normal"
/>
diff --git a/services/tests/PackageManagerServiceTests/host/test-apps/AndroidManifestVersion2.xml b/services/tests/PackageManagerServiceTests/host/test-apps/AndroidManifestVersion2.xml
index 25e9f8eb2a67..620054c5dd57 100644
--- a/services/tests/PackageManagerServiceTests/host/test-apps/AndroidManifestVersion2.xml
+++ b/services/tests/PackageManagerServiceTests/host/test-apps/AndroidManifestVersion2.xml
@@ -16,12 +16,12 @@
-->
<manifest
xmlns:android="http://schemas.android.com/apk/res/android"
- package="com.android.server.pm.test.dummy_app"
+ package="com.android.server.pm.test.test_app"
android:versionCode="2"
>
<permission
- android:name="com.android.server.pm.test.dummy_app.TEST_PERMISSION"
+ android:name="com.android.server.pm.test.test_app.TEST_PERMISSION"
android:protectionLevel="normal"
/>
diff --git a/services/tests/PackageManagerServiceTests/host/test-apps/AndroidManifestVersion3.xml b/services/tests/PackageManagerServiceTests/host/test-apps/AndroidManifestVersion3.xml
index 935f5e62f508..1997771783dd 100644
--- a/services/tests/PackageManagerServiceTests/host/test-apps/AndroidManifestVersion3.xml
+++ b/services/tests/PackageManagerServiceTests/host/test-apps/AndroidManifestVersion3.xml
@@ -16,12 +16,12 @@
-->
<manifest
xmlns:android="http://schemas.android.com/apk/res/android"
- package="com.android.server.pm.test.dummy_app"
+ package="com.android.server.pm.test.test_app"
android:versionCode="3"
>
<permission
- android:name="com.android.server.pm.test.dummy_app.TEST_PERMISSION"
+ android:name="com.android.server.pm.test.test_app.TEST_PERMISSION"
android:protectionLevel="normal"
/>
diff --git a/services/tests/PackageManagerServiceTests/host/test-apps/AndroidManifestVersion4.xml b/services/tests/PackageManagerServiceTests/host/test-apps/AndroidManifestVersion4.xml
index d0643cbb2aeb..d6ade0304189 100644
--- a/services/tests/PackageManagerServiceTests/host/test-apps/AndroidManifestVersion4.xml
+++ b/services/tests/PackageManagerServiceTests/host/test-apps/AndroidManifestVersion4.xml
@@ -16,12 +16,12 @@
-->
<manifest
xmlns:android="http://schemas.android.com/apk/res/android"
- package="com.android.server.pm.test.dummy_app"
+ package="com.android.server.pm.test.test_app"
android:versionCode="4"
>
<permission
- android:name="com.android.server.pm.test.dummy_app.TEST_PERMISSION"
+ android:name="com.android.server.pm.test.test_app.TEST_PERMISSION"
android:protectionLevel="normal"
/>
diff --git a/tests/utils/hostutils/src/com/android/internal/util/test/SystemPreparer.java b/tests/utils/hostutils/src/com/android/internal/util/test/SystemPreparer.java
index f30c35aca8da..c2a5459ae125 100644
--- a/tests/utils/hostutils/src/com/android/internal/util/test/SystemPreparer.java
+++ b/tests/utils/hostutils/src/com/android/internal/util/test/SystemPreparer.java
@@ -34,6 +34,8 @@ import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
+import java.nio.file.Path;
+import java.nio.file.Paths;
import java.util.ArrayList;
import javax.annotation.Nullable;
@@ -49,7 +51,7 @@ import javax.annotation.Nullable;
public class SystemPreparer extends ExternalResource {
private static final long OVERLAY_ENABLE_TIMEOUT_MS = 30000;
- // The paths of the files pushed onto the device through this rule.
+ // The paths of the files pushed onto the device through this rule to be removed after.
private ArrayList<String> mPushedFiles = new ArrayList<>();
// The package names of packages installed through this rule.
@@ -81,7 +83,7 @@ public class SystemPreparer extends ExternalResource {
final ITestDevice device = mDeviceProvider.getDevice();
remount();
assertTrue(device.pushFile(copyResourceToTemp(filePath), outputPath));
- mPushedFiles.add(outputPath);
+ addPushedFile(device, outputPath);
return this;
}
@@ -91,10 +93,23 @@ public class SystemPreparer extends ExternalResource {
final ITestDevice device = mDeviceProvider.getDevice();
remount();
assertTrue(device.pushFile(file, outputPath));
- mPushedFiles.add(outputPath);
+ addPushedFile(device, outputPath);
return this;
}
+ private void addPushedFile(ITestDevice device, String outputPath)
+ throws DeviceNotAvailableException {
+ Path pathCreated = Paths.get(outputPath);
+
+ // Find the top most parent that is new to the device
+ while (pathCreated.getParent() != null
+ && !device.doesFileExist(pathCreated.getParent().toString())) {
+ pathCreated = pathCreated.getParent();
+ }
+
+ mPushedFiles.add(pathCreated.toString());
+ }
+
/** Deletes the given path from the device */
public SystemPreparer deleteFile(String file) throws DeviceNotAvailableException {
final ITestDevice device = mDeviceProvider.getDevice();
@@ -203,7 +218,7 @@ public class SystemPreparer extends ExternalResource {
/** Removes installed packages and files that were pushed to the device. */
@Override
- protected void after() {
+ public void after() {
final ITestDevice device = mDeviceProvider.getDevice();
try {
remount();