Skip to content

Instantly share code, notes, and snippets.

@Anton-V-K
Last active February 23, 2026 22:58
Show Gist options
  • Select an option

  • Save Anton-V-K/4eb0ae1cda7f32cee9cf460526deb4a5 to your computer and use it in GitHub Desktop.

Select an option

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
# 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