Merge "Fix fail verification without cleaning child session data"

This commit is contained in:
Felka Chang
2021-08-16 16:05:40 +00:00
committed by Android (Google) Code Review
6 changed files with 191 additions and 0 deletions

View File

@@ -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);

View File

@@ -20,6 +20,13 @@
<target_preparer class="com.android.tradefed.targetprep.RootTargetPreparer"/>
<target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer">
<option name="run-command"
value="pm uninstall com.android.cts.install.lib.testapp.A" />
<option name="teardown-command"
value="pm uninstall com.android.cts.install.lib.testapp.A" />
</target_preparer>
<test class="com.android.tradefed.testtype.HostTest">
<option name="jar" value="PackageManagerServiceHostTests.jar" />
</test>

View File

@@ -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)
}
}

View File

@@ -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",

View File

@@ -19,6 +19,8 @@
package="com.android.server.pm.test.deviceside">
<uses-permission android:name="android.permission.QUERY_ALL_PACKAGES" />
<uses-permission android:name="android.permission.INSTALL_PACKAGES" />
<uses-permission android:name="android.permission.DELETE_PACKAGES" />
<instrumentation
android:name="androidx.test.runner.AndroidJUnitRunner"

View File

@@ -0,0 +1,120 @@
/*
* 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
import android.Manifest
import android.app.Instrumentation
import android.content.Context
import android.system.helpers.CommandsHelper
import androidx.test.platform.app.InstrumentationRegistry.getInstrumentation
import com.android.compatibility.common.util.AdoptShellPermissionsRule
import com.android.compatibility.common.util.SystemUtil
import com.android.cts.install.lib.Install
import com.android.cts.install.lib.InstallUtils
import com.android.cts.install.lib.LocalIntentSender
import com.android.cts.install.lib.TestApp
import com.google.common.truth.Truth
import org.junit.Rule
import org.junit.Test
import java.io.IOException
import kotlin.jvm.JvmField
class PackageInstallerSessionTest {
@Rule
@JvmField
val mAdoptShellPermissionsRule = AdoptShellPermissionsRule(
getInstrumentation().getUiAutomation(),
Manifest.permission.INSTALL_PACKAGES,
Manifest.permission.DELETE_PACKAGES)
private val mInstrumentation: Instrumentation = getInstrumentation()
private var mChildSessionStageDirInfo: String? = null
/**
* To get all of child session IDs.
*
* @param parentSessionId the parent session id
* @return the array of child session IDs
* @throws IOException caused by opening parent session fail.
*/
@Throws(IOException::class)
private fun getChildSessionIds(parentSessionId: Int): IntArray {
InstallUtils.openPackageInstallerSession(parentSessionId).use {
parentSession -> return parentSession.childSessionIds
}
}
private fun getSessionStageDir(sessionId: Int): String? {
val commandsHelper: CommandsHelper = CommandsHelper.getInstance(mInstrumentation)
val dumpsysForPackage: MutableList<String>? = commandsHelper
.executeShellCommandAndSplitOutput("dumpsys package", "\\n")
val pattern = Regex(" stageDir=(\\S+${sessionId}\\S+) ")
val matchStageDirs: ArrayList<String> = 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<String> = 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()
}
}