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