| #!/usr/bin/env python3 | |
| # Copyright (C) 2026 The Android Open Source Project | |
| # | |
| # Licensed under the Apache License, Version 2.0 (the "License"); | |
| # you may not use this file except in compliance with the License. | |
| # You may obtain a copy of the License at | |
| # | |
| # http://www.apache.org/licenses/LICENSE-2.0 | |
| # | |
| # Unless required by applicable law or agreed to in writing, software | |
| # distributed under the License is distributed on an "AS IS" BASIS, | |
| # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
| # See the License for the specific language governing permissions and | |
| # limitations under the License. | |
| import argparse | |
| import base64 | |
| import os | |
| import textwrap | |
| # The JVM specification strictly limits the bytecode of any single method (including | |
| # static initializers `<clinit>`) to 65535 bytes. A simple `{ (byte)x, ... }` byte array | |
| # generates push and store bytecode instructions per element. While the absolute | |
| # mathematical threshold for standard javac is ~10,943 elements, we conservatively cap | |
| # this at 8192 (8 KB) to guarantee safety across different compiler versions and pipelines. | |
| JAVA_ARRAY_BYTE_LIMIT = 8192 | |
| TEMPLATE = """/* | |
| * Copyright (C) 2026 The Android Open Source Project | |
| * | |
| * Licensed under the Apache License, Version 2.0 (the "License"); | |
| * you may not use this file except in compliance with the License. | |
| * You may obtain a copy of the License at | |
| * | |
| * http://www.apache.org/licenses/LICENSE-2.0 | |
| * | |
| * Unless required by applicable law or agreed to in writing, software | |
| * distributed under the License is distributed on an "AS IS" BASIS, | |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
| * See the License for the specific language governing permissions and | |
| * limitations under the License. | |
| */ | |
| package {package}; | |
| /** | |
| * Autogenerated by java_blob_emitter.py | |
| */ | |
| public class {classname} {{ | |
| {class_body} | |
| }} | |
| """ | |
| def generate_code(package: str, classname: str, data: bytes) -> str: | |
| # Store data as byte array literal | |
| if len(data) <= JAVA_ARRAY_BYTE_LIMIT: | |
| bytes = [f"(byte)0x{b:02x}" for b in data] | |
| bytes_joined = ", ".join(bytes) | |
| bytes_wrapped = textwrap.fill( | |
| bytes_joined, | |
| width=100, | |
| initial_indent=" ", | |
| subsequent_indent=" ") | |
| class_body = " private static final byte[] BYTES = {\n" | |
| class_body += bytes_wrapped + "\n" | |
| class_body += " };\n\n" | |
| class_body += " public static byte[] getBytes() {\n" | |
| class_body += " return BYTES;\n" | |
| class_body += " }" | |
| # Store data as base64-encoded string | |
| else: | |
| bytes_base64 = base64.b64encode(data).decode('utf-8') | |
| class_body = f" private static final String BYTES_BASE64 = \"{bytes_base64}\";\n\n" | |
| class_body += " public static byte[] getBytes() {\n" | |
| class_body += " return android.util.Base64.decode(BYTES_BASE64, android.util.Base64.DEFAULT);\n" | |
| class_body += " }" | |
| return TEMPLATE.format( | |
| package=package, classname=classname, class_body=class_body) | |
| def main(): | |
| parser = argparse.ArgumentParser() | |
| parser.add_argument('--output', required=True) | |
| parser.add_argument('--package', required=True) | |
| parser.add_argument('input') | |
| args = parser.parse_args() | |
| with open(args.input, 'rb') as f: | |
| data = f.read() | |
| classname = os.path.basename(args.output).split('.', 1)[0] | |
| code = generate_code(args.package, classname, data) | |
| with open(args.output, 'w') as f: | |
| f.write(code) | |
| if __name__ == '__main__': | |
| main() |