You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
111 lines
3.4 KiB
111 lines
3.4 KiB
"""
|
|
Parses @AndroidManifestXml annotations to pre-generate APKs for tests
|
|
|
|
Assumes a Kotlin triple quoted string literal which is prepended by a custom annotation. See the
|
|
AndroidManifestXml annotation for more information.
|
|
"""
|
|
|
|
import subprocess
|
|
import sys
|
|
import os
|
|
|
|
ANNOTATION = "@AndroidManifestXml"
|
|
TRIPLE_QUOTE = "\"\"\""
|
|
PACKAGE_NAME_PREFIX = "android.content.pm.parsing.cts.generated"
|
|
GENERATED_APK_PACKAGE_NAMES_FILE = "GeneratedApkPackageNames.txt"
|
|
ANDROID_NAMESPACE = "xmlns:android=\"http://schemas.android.com/apk/res/android\""
|
|
|
|
def java_string_hashcode(string):
|
|
"""
|
|
Simulates Java's hashCode so that APKs can be looked up at runtime by using the manifest
|
|
hashCode as the file name.
|
|
"""
|
|
hash = 0
|
|
for char in string:
|
|
hash = int((((31 * hash + ord(char)) ^ 0x80000000) & 0xFFFFFFFF) - 0x80000000)
|
|
return str(abs(hash))
|
|
|
|
aapt2 = sys.argv[1]
|
|
frameworkRes = sys.argv[2]
|
|
apkSigner = sys.argv[3]
|
|
keyStore = sys.argv[4]
|
|
genDir = sys.argv[5]
|
|
inputFiles = sys.argv[6:]
|
|
|
|
tempDir = f"{genDir}/temp"
|
|
outDir = f"{genDir}/out"
|
|
|
|
os.makedirs(tempDir, exist_ok=True)
|
|
os.makedirs(outDir, exist_ok=True)
|
|
|
|
packageNamesOutput = open(f"{outDir}/{GENERATED_APK_PACKAGE_NAMES_FILE}", "w")
|
|
|
|
usedHashCodes = {}
|
|
|
|
for inputFile in inputFiles:
|
|
text = open(inputFile, "r").read()
|
|
|
|
annotationIndex = 0
|
|
while True:
|
|
try:
|
|
annotationIndex = text.index(ANNOTATION, annotationIndex)
|
|
if annotationIndex < 0:
|
|
break
|
|
except:
|
|
break
|
|
|
|
annotationIndex += len(ANNOTATION)
|
|
startIndex = text.index(TRIPLE_QUOTE, annotationIndex)
|
|
if startIndex < 0:
|
|
continue
|
|
|
|
endIndex = text.index(TRIPLE_QUOTE, startIndex + len(TRIPLE_QUOTE))
|
|
if endIndex < 0:
|
|
continue
|
|
|
|
string = text[startIndex + len(TRIPLE_QUOTE): endIndex]
|
|
hashCode = java_string_hashcode(string)
|
|
|
|
if hashCode in usedHashCodes:
|
|
if usedHashCodes[hashCode] != string:
|
|
sys.exit(f"Manifest XML with hash code {hashCode} already used: {string}")
|
|
usedHashCodes[hashCode] = string
|
|
|
|
if ANDROID_NAMESPACE not in string:
|
|
string = string.replace("<manifest", f"<manifest {ANDROID_NAMESPACE}\n", 1)
|
|
|
|
packageName = PACKAGE_NAME_PREFIX + hashCode
|
|
string = string.replace(">", f"\npackage=\"{packageName}\"\n>", 1)
|
|
string = string.replace("<application", "<application\nandroid:hasCode=\"false\"\n")
|
|
|
|
outputPath = f"{tempDir}/{hashCode}.xml"
|
|
outputFile = open(outputPath, "w")
|
|
outputFile.write(string)
|
|
outputFile.close()
|
|
|
|
packageNamesOutput.write(packageName)
|
|
packageNamesOutput.write("\n")
|
|
|
|
apkPath = f"{outDir}/{hashCode}.apk"
|
|
|
|
subprocess.run([
|
|
aapt2, "link",
|
|
"-I", frameworkRes,
|
|
"--manifest", outputPath,
|
|
"--warn-manifest-validation",
|
|
"--rename-manifest-package", packageName,
|
|
"-o", apkPath
|
|
], check = True)
|
|
|
|
subprocess.run([
|
|
apkSigner, "sign",
|
|
"--ks", keyStore,
|
|
"--ks-pass", "pass:password",
|
|
apkPath
|
|
], check = True)
|
|
|
|
# apksigner will generate an idsig file, but it's not useful for the test, so get rid of it
|
|
idsigPath = f"{outDir}/{hashCode}.idsig"
|
|
if os.path.isfile(idsigPath):
|
|
os.remove(idsigPath)
|