xref: /aosp_15_r20/cts/hostsidetests/packagemanager/parsing/processing/ApkGenerator.py (revision b7c941bb3fa97aba169d73cee0bed2de8ac964bf)
1*b7c941bbSAndroid Build Coastguard Worker"""
2*b7c941bbSAndroid Build Coastguard WorkerParses @AndroidManifestXml annotations to pre-generate APKs for tests
3*b7c941bbSAndroid Build Coastguard Worker
4*b7c941bbSAndroid Build Coastguard WorkerAssumes a Kotlin triple quoted string literal which is prepended by a custom annotation. See the
5*b7c941bbSAndroid Build Coastguard WorkerAndroidManifestXml annotation for more information.
6*b7c941bbSAndroid Build Coastguard Worker"""
7*b7c941bbSAndroid Build Coastguard Worker
8*b7c941bbSAndroid Build Coastguard Workerimport subprocess
9*b7c941bbSAndroid Build Coastguard Workerimport sys
10*b7c941bbSAndroid Build Coastguard Workerimport os
11*b7c941bbSAndroid Build Coastguard Worker
12*b7c941bbSAndroid Build Coastguard WorkerANNOTATION = "@AndroidManifestXml"
13*b7c941bbSAndroid Build Coastguard WorkerTRIPLE_QUOTE = "\"\"\""
14*b7c941bbSAndroid Build Coastguard WorkerPACKAGE_NAME_PREFIX = "android.content.pm.parsing.cts.generated"
15*b7c941bbSAndroid Build Coastguard WorkerGENERATED_APK_PACKAGE_NAMES_FILE = "GeneratedApkPackageNames.txt"
16*b7c941bbSAndroid Build Coastguard WorkerANDROID_NAMESPACE = "xmlns:android=\"http://schemas.android.com/apk/res/android\""
17*b7c941bbSAndroid Build Coastguard Worker
18*b7c941bbSAndroid Build Coastguard Workerdef java_string_hashcode(string):
19*b7c941bbSAndroid Build Coastguard Worker    """
20*b7c941bbSAndroid Build Coastguard Worker        Simulates Java's hashCode so that APKs can be looked up at runtime by using the manifest
21*b7c941bbSAndroid Build Coastguard Worker        hashCode as the file name.
22*b7c941bbSAndroid Build Coastguard Worker    """
23*b7c941bbSAndroid Build Coastguard Worker    hash = 0
24*b7c941bbSAndroid Build Coastguard Worker    for char in string:
25*b7c941bbSAndroid Build Coastguard Worker        hash = int((((31 * hash + ord(char)) ^ 0x80000000) & 0xFFFFFFFF) - 0x80000000)
26*b7c941bbSAndroid Build Coastguard Worker    return str(abs(hash))
27*b7c941bbSAndroid Build Coastguard Worker
28*b7c941bbSAndroid Build Coastguard Workeraapt2 = sys.argv[1]
29*b7c941bbSAndroid Build Coastguard WorkerframeworkRes = sys.argv[2]
30*b7c941bbSAndroid Build Coastguard WorkerapkSigner = sys.argv[3]
31*b7c941bbSAndroid Build Coastguard WorkerkeyStore = sys.argv[4]
32*b7c941bbSAndroid Build Coastguard WorkergenDir = sys.argv[5]
33*b7c941bbSAndroid Build Coastguard WorkerinputFiles = sys.argv[6:]
34*b7c941bbSAndroid Build Coastguard Worker
35*b7c941bbSAndroid Build Coastguard WorkertempDir = f"{genDir}/temp"
36*b7c941bbSAndroid Build Coastguard WorkeroutDir = f"{genDir}/out"
37*b7c941bbSAndroid Build Coastguard Worker
38*b7c941bbSAndroid Build Coastguard Workeros.makedirs(tempDir, exist_ok=True)
39*b7c941bbSAndroid Build Coastguard Workeros.makedirs(outDir, exist_ok=True)
40*b7c941bbSAndroid Build Coastguard Worker
41*b7c941bbSAndroid Build Coastguard WorkerpackageNamesOutput = open(f"{outDir}/{GENERATED_APK_PACKAGE_NAMES_FILE}", "w")
42*b7c941bbSAndroid Build Coastguard Worker
43*b7c941bbSAndroid Build Coastguard WorkerusedHashCodes = {}
44*b7c941bbSAndroid Build Coastguard Worker
45*b7c941bbSAndroid Build Coastguard Workerfor inputFile in inputFiles:
46*b7c941bbSAndroid Build Coastguard Worker    text = open(inputFile, "r").read()
47*b7c941bbSAndroid Build Coastguard Worker
48*b7c941bbSAndroid Build Coastguard Worker    annotationIndex = 0
49*b7c941bbSAndroid Build Coastguard Worker    while True:
50*b7c941bbSAndroid Build Coastguard Worker        try:
51*b7c941bbSAndroid Build Coastguard Worker            annotationIndex = text.index(ANNOTATION, annotationIndex)
52*b7c941bbSAndroid Build Coastguard Worker            if annotationIndex < 0:
53*b7c941bbSAndroid Build Coastguard Worker                break
54*b7c941bbSAndroid Build Coastguard Worker        except:
55*b7c941bbSAndroid Build Coastguard Worker            break
56*b7c941bbSAndroid Build Coastguard Worker
57*b7c941bbSAndroid Build Coastguard Worker        annotationIndex += len(ANNOTATION)
58*b7c941bbSAndroid Build Coastguard Worker        startIndex = text.index(TRIPLE_QUOTE, annotationIndex)
59*b7c941bbSAndroid Build Coastguard Worker        if startIndex < 0:
60*b7c941bbSAndroid Build Coastguard Worker            continue
61*b7c941bbSAndroid Build Coastguard Worker
62*b7c941bbSAndroid Build Coastguard Worker        endIndex = text.index(TRIPLE_QUOTE, startIndex + len(TRIPLE_QUOTE))
63*b7c941bbSAndroid Build Coastguard Worker        if endIndex < 0:
64*b7c941bbSAndroid Build Coastguard Worker            continue
65*b7c941bbSAndroid Build Coastguard Worker
66*b7c941bbSAndroid Build Coastguard Worker        string = text[startIndex + len(TRIPLE_QUOTE): endIndex]
67*b7c941bbSAndroid Build Coastguard Worker        hashCode = java_string_hashcode(string)
68*b7c941bbSAndroid Build Coastguard Worker
69*b7c941bbSAndroid Build Coastguard Worker        if hashCode in usedHashCodes:
70*b7c941bbSAndroid Build Coastguard Worker            if usedHashCodes[hashCode] != string:
71*b7c941bbSAndroid Build Coastguard Worker                sys.exit(f"Manifest XML with hash code {hashCode} already used: {string}")
72*b7c941bbSAndroid Build Coastguard Worker        usedHashCodes[hashCode] = string
73*b7c941bbSAndroid Build Coastguard Worker
74*b7c941bbSAndroid Build Coastguard Worker        if ANDROID_NAMESPACE not in string:
75*b7c941bbSAndroid Build Coastguard Worker            string = string.replace("<manifest", f"<manifest {ANDROID_NAMESPACE}\n", 1)
76*b7c941bbSAndroid Build Coastguard Worker
77*b7c941bbSAndroid Build Coastguard Worker        packageName = PACKAGE_NAME_PREFIX + hashCode
78*b7c941bbSAndroid Build Coastguard Worker        string = string.replace(">", f"\npackage=\"{packageName}\"\n>", 1)
79*b7c941bbSAndroid Build Coastguard Worker        string = string.replace("<application", "<application\nandroid:hasCode=\"false\"\n")
80*b7c941bbSAndroid Build Coastguard Worker
81*b7c941bbSAndroid Build Coastguard Worker        outputPath = f"{tempDir}/{hashCode}.xml"
82*b7c941bbSAndroid Build Coastguard Worker        outputFile = open(outputPath, "w")
83*b7c941bbSAndroid Build Coastguard Worker        outputFile.write(string)
84*b7c941bbSAndroid Build Coastguard Worker        outputFile.close()
85*b7c941bbSAndroid Build Coastguard Worker
86*b7c941bbSAndroid Build Coastguard Worker        packageNamesOutput.write(packageName)
87*b7c941bbSAndroid Build Coastguard Worker        packageNamesOutput.write("\n")
88*b7c941bbSAndroid Build Coastguard Worker
89*b7c941bbSAndroid Build Coastguard Worker        apkPath = f"{outDir}/{hashCode}.apk"
90*b7c941bbSAndroid Build Coastguard Worker
91*b7c941bbSAndroid Build Coastguard Worker        subprocess.run([
92*b7c941bbSAndroid Build Coastguard Worker            aapt2, "link",
93*b7c941bbSAndroid Build Coastguard Worker            "-I", frameworkRes,
94*b7c941bbSAndroid Build Coastguard Worker            "--manifest", outputPath,
95*b7c941bbSAndroid Build Coastguard Worker            "--warn-manifest-validation",
96*b7c941bbSAndroid Build Coastguard Worker            "--rename-manifest-package", packageName,
97*b7c941bbSAndroid Build Coastguard Worker            "-o", apkPath
98*b7c941bbSAndroid Build Coastguard Worker        ], check = True)
99*b7c941bbSAndroid Build Coastguard Worker
100*b7c941bbSAndroid Build Coastguard Worker        subprocess.run([
101*b7c941bbSAndroid Build Coastguard Worker            apkSigner, "sign",
102*b7c941bbSAndroid Build Coastguard Worker            "--ks", keyStore,
103*b7c941bbSAndroid Build Coastguard Worker            "--ks-pass", "pass:password",
104*b7c941bbSAndroid Build Coastguard Worker            apkPath
105*b7c941bbSAndroid Build Coastguard Worker        ], check = True)
106*b7c941bbSAndroid Build Coastguard Worker
107*b7c941bbSAndroid Build Coastguard Worker        # apksigner will generate an idsig file, but it's not useful for the test, so get rid of it
108*b7c941bbSAndroid Build Coastguard Worker        idsigPath = f"{outDir}/{hashCode}.idsig"
109*b7c941bbSAndroid Build Coastguard Worker        if os.path.isfile(idsigPath):
110*b7c941bbSAndroid Build Coastguard Worker            os.remove(idsigPath)
111