Last active
February 23, 2026 22:58
-
-
Save Anton-V-K/4eb0ae1cda7f32cee9cf460526deb4a5 to your computer and use it in GitHub Desktop.
Python script to check validity of APK-files in a directory
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| # V4 (c) https://chatgpt.com/share/6991a15b-fb88-8001-bf63-7fb85a816328 | |
| # Patch 1: less androguard diagnostics messages | |
| # Patch 2: better splits detection | |
| # pip install androguard | |
| import os | |
| import argparse | |
| from androguard.core.apk import APK | |
| from androguard.util import set_log | |
| ANDROID_NS = "http://schemas.android.com/apk/res/android" | |
| def get_android_attr(element, attr): | |
| """Helper to read android namespace attributes.""" | |
| return element.get(f"{{{ANDROID_NS}}}{attr}") | |
| def has_split_manifest_attr(manifest): | |
| """Check <manifest split="...">""" | |
| return manifest.get("split") is not None | |
| def has_is_split_required(application): | |
| """Check android:isSplitRequired on <application>""" | |
| if application is None: | |
| return False | |
| val = get_android_attr(application, "isSplitRequired") | |
| return val == "true" | |
| def has_vending_split_metadata(application): | |
| """ | |
| Check meta-data entries like: | |
| com.android.vending.splits.required | |
| """ | |
| if application is None: | |
| return False | |
| for child in application.findall("meta-data"): | |
| name = get_android_attr(child, "name") | |
| res = get_android_attr(child, "resource") | |
| value = get_android_attr(child, "value") | |
| if name == "com.android.vending.splits.required": | |
| if value == 'true': | |
| return True | |
| if name == "com.android.vending.splits": | |
| # android:resource="@xml/splits0" is OK | |
| continue | |
| # Optional: detect other known split metadata | |
| if name and "splits" in name.lower(): | |
| if value == 'true': | |
| return True | |
| ''' ??? | |
| if value and "splits" in value.lower(): | |
| return True | |
| ''' | |
| return False | |
| def is_split_apk(apk_path): | |
| try: | |
| apk = APK(apk_path) | |
| manifest = apk.get_android_manifest_xml() | |
| if manifest is None: | |
| return False | |
| # 1️⃣ Root manifest split attr | |
| if has_split_manifest_attr(manifest): | |
| return True | |
| application = manifest.find("application") | |
| # 2️⃣ application android:isSplitRequired | |
| if has_is_split_required(application): | |
| return True | |
| # 3️⃣ Play vending split metadata | |
| if has_vending_split_metadata(application): | |
| return True | |
| return False | |
| except Exception as e: | |
| print(f"Error reading {apk_path}: {e}") | |
| return False | |
| def find_split_apks(directory): | |
| results = [] | |
| for root, _, files in os.walk(directory): | |
| for f in files: | |
| if f.lower().endswith(".apk"): | |
| full_path = os.path.join(root, f) | |
| if is_split_apk(full_path): | |
| results.append(full_path) | |
| return results | |
| def main(): | |
| # (c) https://github.com/androguard/androguard/issues/1052#issuecomment-2123943458 | |
| set_log("ERROR") | |
| parser = argparse.ArgumentParser( | |
| description="Detect split APKs using multiple manifest signals" | |
| ) | |
| parser.add_argument("directory", help="Directory containing APK files") | |
| args = parser.parse_args() | |
| directory = os.path.abspath(args.directory) | |
| if not os.path.isdir(directory): | |
| print("Invalid directory") | |
| return | |
| split_apks = find_split_apks(directory) | |
| print(f"\nFound {len(split_apks)} split APK(s):\n") | |
| for apk in split_apks: | |
| print(apk) | |
| if __name__ == "__main__": | |
| main() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment