diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java index 3d63dcf7868ff..958c769ba7d19 100644 --- a/services/core/java/com/android/server/pm/PackageInstallerSession.java +++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java @@ -2112,6 +2112,11 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { Slog.e(TAG, "Failed to verify session " + sessionId + " [" + msgWithErrorCode + "]"); // Session is sealed and committed but could not be verified, we need to destroy it. destroyInternal(); + if (isMultiPackage()) { + for (PackageInstallerSession childSession : getChildSessions()) { + childSession.destroyInternal(); + } + } if (isStaged()) { mStagedSession.setSessionFailed( SessionInfo.STAGED_SESSION_VERIFICATION_FAILED, msgWithErrorCode); diff --git a/services/tests/PackageManagerServiceTests/host/AndroidTest.xml b/services/tests/PackageManagerServiceTests/host/AndroidTest.xml index dc8c8113fac2e..f584599e81006 100644 --- a/services/tests/PackageManagerServiceTests/host/AndroidTest.xml +++ b/services/tests/PackageManagerServiceTests/host/AndroidTest.xml @@ -20,6 +20,13 @@ + + + diff --git a/services/tests/PackageManagerServiceTests/host/src/com/android/server/pm/test/PackageInstallerSessionTest.kt b/services/tests/PackageManagerServiceTests/host/src/com/android/server/pm/test/PackageInstallerSessionTest.kt new file mode 100644 index 0000000000000..86571abcbb380 --- /dev/null +++ b/services/tests/PackageManagerServiceTests/host/src/com/android/server/pm/test/PackageInstallerSessionTest.kt @@ -0,0 +1,55 @@ +/* + * Copyright (C) 2021 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.tradefed.testtype.DeviceJUnit4ClassRunner +import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test +import com.google.common.truth.Truth +import org.junit.Rule +import org.junit.Test +import org.junit.rules.TemporaryFolder +import org.junit.runner.RunWith +import kotlin.jvm.JvmField + +@RunWith(DeviceJUnit4ClassRunner::class) +class PackageInstallerSessionTest : BaseHostJUnit4Test() { + companion object { + private const val DEVICE_SIDE = "PackageManagerServiceDeviceSideTests.apk" + } + + @Rule + @JvmField + val tempFolder = TemporaryFolder() + + @Test + fun verify_parentSessionFail_childSessionFiles_shouldBeDestroyed() { + runDeviceTest("com.android.server.pm.PackageInstallerSessionTest", + "verify_parentSessionFail_childSessionFiles_shouldBeDestroyed") + } + + /** + * Run a device side test from com.android.server.pm.test.deviceside.DeviceSide + * + * @param method the method to run + */ + fun runDeviceTest(testClassName: String, method: String) { + val deviceSideFile = HostUtils.copyResourceToHostFile(DEVICE_SIDE, tempFolder.newFile()) + Truth.assertThat(device.installPackage(deviceSideFile, true)).isNull() + runDeviceTests(device, "com.android.server.pm.test.deviceside", + testClassName, method) + } +} diff --git a/services/tests/PackageManagerServiceTests/host/test-apps/DeviceSide/Android.bp b/services/tests/PackageManagerServiceTests/host/test-apps/DeviceSide/Android.bp index 7e4f0e72b62d0..53adc2fb00e69 100644 --- a/services/tests/PackageManagerServiceTests/host/test-apps/DeviceSide/Android.bp +++ b/services/tests/PackageManagerServiceTests/host/test-apps/DeviceSide/Android.bp @@ -32,6 +32,8 @@ android_test_helper_app { ], static_libs: [ "androidx.annotation_annotation", + "commands-helper", + "cts-install-lib", "junit", "junit-params", "androidx.test.ext.junit", diff --git a/services/tests/PackageManagerServiceTests/host/test-apps/DeviceSide/AndroidManifest.xml b/services/tests/PackageManagerServiceTests/host/test-apps/DeviceSide/AndroidManifest.xml index 286ad56435fdd..dd6dea78d08b0 100644 --- a/services/tests/PackageManagerServiceTests/host/test-apps/DeviceSide/AndroidManifest.xml +++ b/services/tests/PackageManagerServiceTests/host/test-apps/DeviceSide/AndroidManifest.xml @@ -19,6 +19,8 @@ package="com.android.server.pm.test.deviceside"> + + return parentSession.childSessionIds + } + } + + private fun getSessionStageDir(sessionId: Int): String? { + val commandsHelper: CommandsHelper = CommandsHelper.getInstance(mInstrumentation) + val dumpsysForPackage: MutableList? = commandsHelper + .executeShellCommandAndSplitOutput("dumpsys package", "\\n") + val pattern = Regex(" stageDir=(\\S+${sessionId}\\S+) ") + val matchStageDirs: ArrayList = ArrayList() + dumpsysForPackage?.forEach { line -> + val matchResult: MatchResult? = pattern.find(line) + if (matchResult != null) { + val (stageDir: String) = matchResult.destructured + matchStageDirs.add(stageDir) + } + } + + if (matchStageDirs.size > 0) { + return matchStageDirs[0] + } + return null + } + + private fun getSessionStageDirInfo(sessionId: Int): String? { + SystemUtil.runWithShellPermissionIdentity { + val sessionStageDir = + getSessionStageDir(sessionId) ?: return@runWithShellPermissionIdentity + val command = "su root ls $sessionStageDir" + val lines: List = CommandsHelper.getInstance(mInstrumentation) + .executeShellCommandAndSplitOutput(command, "\\n") + val sessionIdStr = sessionId.toString() + for (line in lines) { + if (line.contains(sessionIdStr)) { + mChildSessionStageDirInfo = line + } + } + } + return mChildSessionStageDirInfo + } + + @Test + @Throws(Exception::class) + fun verify_parentSessionFail_childSessionFiles_shouldBeDestroyed() { + val context: Context = mInstrumentation.targetContext + Install.single(TestApp.A3).commit() + val parentSessionId: Int = Install.multi(TestApp.A1).createSession() + val childSessionIds: IntArray = getChildSessionIds(parentSessionId) + val firstChildSessionId = childSessionIds[0] + + val sender = LocalIntentSender() + try { + InstallUtils.openPackageInstallerSession(parentSessionId).use { session -> + session.commit(sender.intentSender) + val result = sender.result + InstallUtils.assertStatusFailure(result) + } + } finally { + context.unregisterReceiver(sender) + } + + Truth.assertThat(getSessionStageDirInfo(firstChildSessionId)).isNull() + } +}