1 /* <lambda>null2 * Copyright (C) 2021 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 android.content.pm.parsing.cts.generator.api 18 19 import com.android.tradefed.device.ITestDevice 20 import org.junit.BeforeClass 21 import org.junit.ClassRule 22 import org.junit.rules.TemporaryFolder 23 import java.io.File 24 import java.io.FileOutputStream 25 import java.util.zip.ZipFile 26 import kotlin.math.absoluteValue 27 28 /** 29 * Handles installing APKs previously generated via [AndroidManifestXml] declarations. Supports 30 * installing APKs from the annotated strings and cleaning up all packages afterwards. 31 */ 32 object ApkGenerator { 33 34 private const val PACKAGE_NAME_PREFIX = "android.content.pm.parsing.cts.generated" 35 private const val GENERATED_APKS_FILE = "GeneratedApks.zip" 36 private const val GENERATED_PACKAGE_NAMES_FILE = "GeneratedApkPackageNames.txt" 37 38 private lateinit var hostZipFile: File 39 40 /** 41 * Call in a @[BeforeClass] method to preload the zip file to the host. Note that this will 42 * require that the [TemporaryFolder] be initialized as a @[ClassRule]. 43 */ 44 fun initialize(tempFolder: TemporaryFolder) { 45 copyHostZipFileIfNeeded(tempFolder) 46 } 47 48 fun uninstallAll(device: ITestDevice, tempFolder: TemporaryFolder) { 49 getFileFromZip(tempFolder, GENERATED_PACKAGE_NAMES_FILE).readLines().forEach { 50 try { 51 device.uninstallPackage(it) 52 } catch (ignored: Exception) { 53 } 54 } 55 } 56 57 fun install( 58 device: ITestDevice, 59 @AndroidManifestXml 60 @org.intellij.lang.annotations.Language("XML") 61 androidManifestXml: String, 62 tempFolder: TemporaryFolder 63 ): InstallResult { 64 val hashCode = androidManifestXml.hashCode().absoluteValue 65 val apkFile = getFileFromZip(tempFolder, "$hashCode.apk") 66 val error = device.installPackage(apkFile, false, "--bypass-low-target-sdk-block") 67 return InstallResult( 68 packageName = "$PACKAGE_NAME_PREFIX$hashCode", 69 error = error.orEmpty(), 70 ) 71 } 72 73 private fun getFileFromZip(tempFolder: TemporaryFolder, fileName: String): File { 74 val zipFile = ZipFile(copyHostZipFileIfNeeded(tempFolder)) 75 val entry = zipFile.getEntry(fileName) 76 return tempFolder.newFile().apply { 77 zipFile.getInputStream(entry).use { input -> 78 FileOutputStream(this).use { output -> 79 input.copyTo(output) 80 } 81 } 82 } 83 } 84 85 private fun copyHostZipFileIfNeeded(tempFolder: TemporaryFolder): File { 86 if (this::hostZipFile.isInitialized && hostZipFile.exists()) { 87 return hostZipFile 88 } 89 90 val outputFile = tempFolder.newFile() 91 ApkGenerator::class.java.classLoader!!.getResource(GENERATED_APKS_FILE)!!.openStream() 92 .use { input -> 93 FileOutputStream(outputFile).use { output -> 94 input.copyTo(output) 95 } 96 } 97 98 hostZipFile = outputFile 99 return hostZipFile 100 } 101 102 data class InstallResult( 103 val packageName: String, 104 val error: String, 105 ) 106 } 107