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