From 7e58a847acd8178a506df101f034c7ab733733d3 Mon Sep 17 00:00:00 2001 From: Winson Date: Mon, 15 Jun 2020 13:03:58 -0700 Subject: [PATCH] Clean up permissions when system app fails to scan When a system APK fails to scan, the full uninstall flow is not called for the app. So if it had a data variant or was previously scanned before a system update, residual data can remain. This change only fixes up leftover permissions, and there could be other unhandled cases. Specifically this fixes the case where an OTA updates a system app to a higher version that the data variant, but the APK fails to scan due to invalid signature verification. This would cause the app to be removed from the deviced entirely while leaving a declared permission inside PermissionSettings which was serialized to/from disk. This permission would be checked when trying to manually install an update, which would verify against a non-existent package, failing the install. Because of the serialization, a reboot would not be enough to fix this case. This reboot issue is technically still a problem if the permission clean up fails for any reason. Perhaps a future refactor can address the need to seriailize the permissions at all, and only write the necessary state, removing state that doesn't have a valid entry inside a known package. If this case is ever hit, there will be no working application on the device as it's assumed that all system packages will scan. The data variant will be dropped. Bug: 158567255 Test: atest com.android.server.pm.test.InvalidNewSystemAppTest Change-Id: I7cbb6ac231a211543a6bd42c61e1c74112b81736 --- .../server/pm/PackageManagerService.java | 4 + .../host/Android.bp | 8 ++ .../PackageManagerDummyAppVersion3Invalid.apk | Bin 0 -> 2457 bytes .../com/android/server/pm/test/HostUtils.kt | 10 +++ .../server/pm/test/InvalidNewSystemAppTest.kt | 83 ++++++++++++++++++ .../host/test-apps/Android.bp | 5 ++ .../test-apps/AndroidManifestVersion1.xml | 9 +- .../test-apps/AndroidManifestVersion2.xml | 9 +- .../test-apps/AndroidManifestVersion3.xml | 9 +- .../test-apps/AndroidManifestVersion4.xml | 28 ++++++ 10 files changed, 162 insertions(+), 3 deletions(-) create mode 100644 services/tests/PackageManagerServiceTests/host/resources/PackageManagerDummyAppVersion3Invalid.apk create mode 100644 services/tests/PackageManagerServiceTests/host/src/com/android/server/pm/test/InvalidNewSystemAppTest.kt create mode 100644 services/tests/PackageManagerServiceTests/host/test-apps/AndroidManifestVersion4.xml diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index ae8b3a0e9acc3..4133ccd13a989 100644 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -3175,6 +3175,10 @@ public class PackageManagerService extends IPackageManager.Stub psit.remove(); logCriticalInfo(Log.WARN, "System package " + ps.name + " no longer exists; it's data will be wiped"); + + // Assume package is truly gone and wipe residual permissions. + mPermissionManager.updatePermissions(ps.name, null); + // Actual deletion of code and data will be handled by later // reconciliation step } else { diff --git a/services/tests/PackageManagerServiceTests/host/Android.bp b/services/tests/PackageManagerServiceTests/host/Android.bp index dad001b52b15e..41dfade1a09a3 100644 --- a/services/tests/PackageManagerServiceTests/host/Android.bp +++ b/services/tests/PackageManagerServiceTests/host/Android.bp @@ -28,6 +28,14 @@ java_test_host { ":PackageManagerDummyAppVersion1", ":PackageManagerDummyAppVersion2", ":PackageManagerDummyAppVersion3", + ":PackageManagerDummyAppVersion4", ":PackageManagerDummyAppOriginalOverride", + ":PackageManagerServiceHostTestsResources", ] } + +filegroup { + name: "PackageManagerServiceHostTestsResources", + srcs: [ "resources/*" ], + path: "resources/" +} diff --git a/services/tests/PackageManagerServiceTests/host/resources/PackageManagerDummyAppVersion3Invalid.apk b/services/tests/PackageManagerServiceTests/host/resources/PackageManagerDummyAppVersion3Invalid.apk new file mode 100644 index 0000000000000000000000000000000000000000..127886cf8e9e8c66d00a837f44f406d7c3cbfa11 GIT binary patch literal 2457 zcmWIWW@Zs#W?@}C`$tHT83u`w_RGcYhX=A{(nXQucj=4Ga(7MJK%hZp*8j;g_wtZ2Zb}rM5>-`;;*bU6{ znvy?o)-c;UrYwn0V1LtO_=9ho@=Mzv9Cpp}56pKst(&D*wN6>qD=K`IPq3&5^EJii zoxeQt4@LyVuHSgBRQzn?*UX33N3!@Um#7!aEPAeK`r*rrQ~FnI)~w1{{b0^Z(V&eA zdF($Wa@$m=M&-ZSYWVw&{)VGmcc-k-JR@=TPlUL5WNT^l-ma2nPxl117pL~|-rOna zb4o^;|J1HkKDYIPzsJizxs$e8*3oONZ{w8BWg9CND?hWgE#A0v_Hwb+t84E*zV_Ro z-}3FN#&^o^H`*=Qd?_?;Z^E)&YvqiuGd!E1@uzhIQ+VY0WAYo*&8qcZgfxlDT=EW& zzj^-3zLyr{HYq6g3ze@eX$N$MqthTJz9e$dh z`gl-f*EzYsAUsS;IrI*!Dkzi&wkB3TbMR& zaqwN`o~J)P0xAG*ayUd$jY2ugF^Izf*p-Al5QV6w({z;i{Iu>pEx&@k$bj5aK?s(8yXd)i#530 z4W<_-`K3kG1;(W zIdLlEiPo2?EJcyCBqVcE_1<^ydRq8o_1fFBc|_MfkN#hG$Bb9tK7+#lei!hSkS@E_`z7 zS^CVLnZ`9g@-EIZivRlfvQ6HB5|8)t<%%is))M=Qetz*@EIUu#Yx1M`vzzy5mOSY@ zb?Ugu&Bv}BQmN{5Pd2VuvzY1Sx))km{w!xR`X}!=c%bB8p>iJQnS|bY{pQFrqHlEw|?&*C7J|*7uEn z4$Fz&Ss-frMC_|Vhmy#V?2KzdTRL-sF6_TPt&8KHT>nhXTG^bunIBx{)bL29WoaFq zbuz7JN7Xm>9-ji4z0H0G-(N>`6=iRT&?$VJ?Iro$?8oWqj0ul=nERgXIP9mmeBq1( zF85iTzTx_te)D;j}77Z(As2(rt4 z9sNArT!TaOeBD0#o;mH~t*dvD*IQTX+?n&6gAA@1KY04+=_l_qzFUI|&UkB{I`7lC z^3v)PXCFT0`c`no)X4O5=}8~OLd&^cWHu@)%Np-p*&p1k!p8 z#SE!HUKyCwV<-TtDFO0|8A^b%DGa4Rkz9sKhIpXP0w5f!Kmin3KwtxB^#!{zpF(`m!fDVvgUzm4li3#j1JlEYYjAt0cs?SMi`B)1cn(6a(@f5(XjFu#fs~N z(ID3{ta69~(@;LL6__OjvK96$NUjH45$uLygY%UruqR+NM)+_rVCHCK8-60&fRw*c zZ23MT9&8JYMzTeY8CRwS#vT+bXslrcmL_Ofm=%(R(XwxVH!HBY#lXM@g!aGyc3}tc E0Ey-mLjV8( literal 0 HcmV?d00001 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 4927c45550b5e..490f96d8f4263 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 @@ -19,6 +19,7 @@ package com.android.server.pm.test import com.android.internal.util.test.SystemPreparer 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)) @@ -43,4 +44,13 @@ internal object HostUtils { .resolve(file.nameWithoutExtension) .resolve(file.name) .toString() + + fun copyResourceToHostFile(javaResourceName: String, file: File): File { + javaClass.classLoader!!.getResource(javaResourceName).openStream().use { input -> + FileOutputStream(file).use { output -> + input.copyTo(output) + } + } + return file + } } 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 new file mode 100644 index 0000000000000..98e045d0a203b --- /dev/null +++ b/services/tests/PackageManagerServiceTests/host/src/com/android/server/pm/test/InvalidNewSystemAppTest.kt @@ -0,0 +1,83 @@ +/* + * 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.testtype.DeviceJUnit4ClassRunner +import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test +import com.google.common.truth.Truth.assertThat +import org.junit.After +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 + +@RunWith(DeviceJUnit4ClassRunner::class) +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" + + @get:ClassRule + val deviceRebootRule = SystemPreparer.TestRuleDelegate(true) + } + + private val tempFolder = TemporaryFolder() + private val preparer: SystemPreparer = SystemPreparer(tempFolder, + SystemPreparer.RebootStrategy.START_STOP, deviceRebootRule) { this.device } + + @get:Rule + val rules = RuleChain.outerRule(tempFolder).around(preparer)!! + + @Before + @After + fun uninstallDataPackage() { + device.uninstallPackage(TEST_PKG_NAME) + } + + @Test + fun verify() { + // First, push a system app to the device and then update it so there's a data variant + val filePath = HostUtils.makePathForApk("PackageManagerDummyApp.apk", Partition.PRODUCT) + + preparer.pushResourceFile(VERSION_ONE, filePath) + .reboot() + + val versionTwoFile = HostUtils.copyResourceToHostFile(VERSION_TWO, tempFolder.newFile()) + + assertThat(device.installPackage(versionTwoFile, true)).isNull() + + // Then push a bad update to the system, overwriting the existing file as if an OTA occurred + preparer.deleteFile(filePath) + .pushResourceFile(VERSION_THREE_INVALID, filePath) + .reboot() + + // This will remove the package from the device, which is expected + assertThat(device.getAppPackageInfo(TEST_PKG_NAME)).isNull() + + // Then check that a user would still be able to install the application manually + val versionFourFile = HostUtils.copyResourceToHostFile(VERSION_FOUR, tempFolder.newFile()) + assertThat(device.installPackage(versionFourFile, true)).isNull() + } +} diff --git a/services/tests/PackageManagerServiceTests/host/test-apps/Android.bp b/services/tests/PackageManagerServiceTests/host/test-apps/Android.bp index 9568faa7dfd09..c9b29275a7319 100644 --- a/services/tests/PackageManagerServiceTests/host/test-apps/Android.bp +++ b/services/tests/PackageManagerServiceTests/host/test-apps/Android.bp @@ -27,6 +27,11 @@ android_test_helper_app { manifest: "AndroidManifestVersion3.xml" } +android_test_helper_app { + name: "PackageManagerDummyAppVersion4", + manifest: "AndroidManifestVersion4.xml" +} + android_test_helper_app { name: "PackageManagerDummyAppOriginalOverride", manifest: "AndroidManifestOriginalOverride.xml" diff --git a/services/tests/PackageManagerServiceTests/host/test-apps/AndroidManifestVersion1.xml b/services/tests/PackageManagerServiceTests/host/test-apps/AndroidManifestVersion1.xml index d772050d7fd06..b492a31349fc2 100644 --- a/services/tests/PackageManagerServiceTests/host/test-apps/AndroidManifestVersion1.xml +++ b/services/tests/PackageManagerServiceTests/host/test-apps/AndroidManifestVersion1.xml @@ -18,4 +18,11 @@ xmlns:android="http://schemas.android.com/apk/res/android" package="com.android.server.pm.test.dummy_app" android:versionCode="1" - /> + > + + + + diff --git a/services/tests/PackageManagerServiceTests/host/test-apps/AndroidManifestVersion2.xml b/services/tests/PackageManagerServiceTests/host/test-apps/AndroidManifestVersion2.xml index 53f836b222e69..25e9f8eb2a673 100644 --- a/services/tests/PackageManagerServiceTests/host/test-apps/AndroidManifestVersion2.xml +++ b/services/tests/PackageManagerServiceTests/host/test-apps/AndroidManifestVersion2.xml @@ -18,4 +18,11 @@ xmlns:android="http://schemas.android.com/apk/res/android" package="com.android.server.pm.test.dummy_app" android:versionCode="2" - /> + > + + + + diff --git a/services/tests/PackageManagerServiceTests/host/test-apps/AndroidManifestVersion3.xml b/services/tests/PackageManagerServiceTests/host/test-apps/AndroidManifestVersion3.xml index 90ca9d0ac02c7..935f5e62f5082 100644 --- a/services/tests/PackageManagerServiceTests/host/test-apps/AndroidManifestVersion3.xml +++ b/services/tests/PackageManagerServiceTests/host/test-apps/AndroidManifestVersion3.xml @@ -18,4 +18,11 @@ xmlns:android="http://schemas.android.com/apk/res/android" package="com.android.server.pm.test.dummy_app" android:versionCode="3" - /> + > + + + + diff --git a/services/tests/PackageManagerServiceTests/host/test-apps/AndroidManifestVersion4.xml b/services/tests/PackageManagerServiceTests/host/test-apps/AndroidManifestVersion4.xml new file mode 100644 index 0000000000000..d0643cbb2aeb4 --- /dev/null +++ b/services/tests/PackageManagerServiceTests/host/test-apps/AndroidManifestVersion4.xml @@ -0,0 +1,28 @@ + + + + + + +