Skip to main content

apkfile logo

apkfile • Read, inspect, and install Android app packages

PyPI Version Downloads Python Versions Tests pre-commit.ci status Coverage License Code Quality

apkfile reads metadata out of Android .apk, .apkm, .xapk, and .apks files — package name, version, permissions, supported ABIs/languages/densities, icons, signing certificates, manifest security posture (exported components, deep links, dangerous permissions), size/DEX composition, and more — and can install them to a connected device over adb, or diff two apks against each other.

Install

pip install -U apkfile
# or
uv add apkfile

Usage

from apkfile import ApkFile, XapkFile, ApkmFile, ApksFile

# Get apk info
apk = ApkFile("/home/david/Downloads/wa.apk")
print(apk.package_name, apk.version_name, apk.version_code)
print(apk.as_dict())

# Signing certificates, manifest security posture, size/DEX composition
print(apk.signing.is_debug_signed, [c.sha256 for c in apk.signing.all_certificates])
print(apk.security.dangerous_permissions, apk.security.unprotected_exported_components)
print(apk.size_breakdown, apk.dex_info)

# Diff two apks (e.g. two versions of the same app)
old, new = ApkFile("wa-1.apk"), ApkFile("wa-2.apk")
result = old.diff(new)
print(result.permissions_added, result.size_delta, result.signing_changed)

# A quick manifest security report
for component in apk.security.unprotected_exported_components:
    print(
        f"{component.type.value} {component.name} is exported with no permission required"
    )
for name in apk.security.dangerous_permissions:
    print(f"requests dangerous permission: {name}")

# Check whether a split apk can run on a given device ABI
from apkfile import Abi

device_abi = Abi.ARM64
print(all(device_abi.is_compatible_with(a) for a in apk.abis))

# Icons are objects, not just paths -- pick one and extract it
icon = apk.best_icon(max_dpi=320)
icon.extract("icon.png")  # or icon.read_bytes()
print(icon.density, icon.bucket)  # e.g. 320 DensityBucket.XHDPI

# Get apkm info — base/splits are read lazily, straight out of the archive
apkm = ApkmFile("/home/david/Downloads/chrome.apkm")
for split in apkm.splits:
    print(split.split_name, split.split_type)
apkm.install(check=True, upgrade=True)

# Get xapk info
xapk = XapkFile("/home/david/Downloads/telegram.xapk")
print(xapk.abis, xapk.permissions, xapk.langs)

# Get apks info
apks = ApksFile("/home/david/Downloads/facebook.apks")
print(apks.base.permissions, apks.md5, apks.sha256)

CLI

apkfile info app.apk              # print an apk/bundle's metadata as JSON
apkfile diff old.apk new.apk      # print the differences between two apks/bundles as JSON
apkfile install app.apk           # install to connected device(s)
apkfile install app.apk --upgrade --installer com.android.vending --adb-path /path/to/adb

How this library works

apkfile parses AndroidManifest.xml and resources.arsc directly, using androguard — a pure-Python library, so there's nothing to install beyond apkfile itself.

  • For the archive formats (.apkm, .xapk, .apks), basic info (package_name, version_name, version_code, ...) comes from the archive's own JSON manifest. Everything else (base, splits, permissions, languages, ABIs, ...) is parsed lazily, directly from the archive's bytes the first time you access it — no disk extraction happens just to read metadata. ApkFile objects obtained this way have path is None until you call .save(path) on them.
  • The library can also install files (optionally checking compatibility first: min_sdk_version, abis, and langs/densities for split apks) using adb — connect a device and call .install(), or use the standalone install_apks() function directly. Installing extracts only what's needed into a temporary directory for the duration of the push, and cleans up automatically afterwards.

Install adb

If you want to use .install(), you need adb.

  • You can manually provide a path to adb: apk.install(adb_path="/path/to/adb").

Migrating from 1.0

  • ApkFile.path (and every bundle's .path) is now a pathlib.Path, not a str. Comparisons against a bare string (apk.path == "/some/path") no longer match — compare against Path("/some/path"), or use str(apk.path).
  • New: .signing (SigningInfo — signing scheme(s) + certificate(s)), .security (SecurityInfo — permissions with AOSP protection levels, exported components, deep links, debuggable/allowBackup/ cleartext-traffic flags), .size_breakdown (SizeBreakdown — size by dex/resources/native libs/assets/...), .dex_info (DexInfo — method/class/string counts), and .diff(other) / apkfile.diff.diff(a, b) for comparing two apks. All available on ApkFile and every bundle class (bundle .signing/.security delegate to the base apk; .size_breakdown/.dex_info sum base + splits).
  • Two behavior-affecting bug fixes, verified against the official Android manifest/NDK docs:
    • Abi.is_compatible_with() no longer claims x86/x86_64 devices can run arm/arm64 code — stock Android has no built-in ARM↔x86 translation layer, so that was always wrong and could have caused install_apks() to push an incompatible native-code split onto an x86 emulator.
    • InstallLocation's default (when android:installLocation isn't declared) is now INTERNAL_ONLY, per the docs — it was previously (incorrectly) reported as AUTO.
  • ExportedComponent gained read_permission/write_permission (for <provider>'s android:readPermission /android:writePermission), and a provider's default exported value is now resolved correctly — it depends on targetSdkVersion (True up to API 16, False from API 17), unlike activities/services/ receivers, whose default instead depends on whether they declare an <intent-filter>.
  • ApkFile.icons is now tuple[Icon, ...] (was dict[int, str]) — each Icon has .density, .bucket (a DensityBucket), .path, and self-serving .read_bytes()/.extract(path) methods. It's also complete now: the old implementation missed anydpi (adaptive icon) and nodpi variants entirely. Use apk.best_icon(max_dpi=...) to pick a single icon the way androguard/Android itself would. ApkFile.supported_screens is now tuple[ScreenSize, ...] (was tuple[str, ...]).
  • New fields: max_sdk_version, form_factors (tuple[FormFactor, ...] — TV/wearable heuristics), SecurityInfo.implied_permissions (permissions Android silently grants under legacy compatibility rules), and on Certificate: public_key_algorithm/public_key_bit_size, canonical_subject/canonical_issuer (Java-X500Principal-compatible identity strings, safe for comparison unlike subject/issuer), and on SigningInfo: has_duplicate_signature_ids (a tamper/verifier-confusion smell).

Migrating from 0.x

apkfile 1.0 is a from-scratch rewrite. The highlights:

  • aapt is gone. Every aapt_path parameter has been removed, as has get_raw_aapt().
  • extract_path, delete_extracted_files(), and the with XapkFile(...) as xf: context-manager pattern are gone — reading metadata never touches disk anymore, so there's nothing to clean up. .install() still uses a temporary directory, but manages it internally.
  • Exceptions are now a proper hierarchy under apkfile.ApkFileError (InvalidApkError, InvalidBundleError, AdbError, AdbNotFoundError) instead of repurposed builtins.
  • Abi, InstallLocation, and SplitType are str enums now — comparisons against plain strings (apk.install_location == "auto") still work.
  • Minimum supported Python version is 3.10.

See CHANGELOG.md for the full list.

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

apkfile-1.0.1.tar.gz (33.7 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

apkfile-1.0.1-py3-none-any.whl (41.5 kB view details)

Uploaded Python 3

File details

Details for the file apkfile-1.0.1.tar.gz.

File metadata

  • Download URL: apkfile-1.0.1.tar.gz
  • Upload date:
  • Size: 33.7 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: uv/0.12.3 {"installer":{"name":"uv","version":"0.12.3","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

File hashes

Hashes for apkfile-1.0.1.tar.gz
Algorithm Hash digest
SHA256 9858974d735ee8670395480cd7be9934d00c3f14829aeb4d87ed74672a3d4647
MD5 bf107f9afae7701e60ca69a1a690f6f6
BLAKE2b-256 4777cf1c1cda330f58c0e558f672a31c7526f6fbfa13f19e3d215c945fe7ca19

See more details on using hashes here.

File details

Details for the file apkfile-1.0.1-py3-none-any.whl.

File metadata

  • Download URL: apkfile-1.0.1-py3-none-any.whl
  • Upload date:
  • Size: 41.5 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: uv/0.12.3 {"installer":{"name":"uv","version":"0.12.3","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

File hashes

Hashes for apkfile-1.0.1-py3-none-any.whl
Algorithm Hash digest
SHA256 61d22373e6420aa1fe0129dde638c95185800e542e3e157e698de5b98c6d87bd
MD5 1aeaadfd4ea10f2e46665b0f13d61557
BLAKE2b-256 83be8a5f0a40bbd9059e2ecceb1c8f48d54b858e5ee004ce3a025e80a9e05bee

See more details on using hashes here.

Release history Release notifications | RSS feed

1.4.2

2 files

1.4.1

2 files

1.4.0

2 files

1.3.0

2 files

1.2.0

2 files

1.1.0

2 files

This release

1.0.1 This release

2 files

1.0.0

2 files

0.1.7

2 files

0.1.6

1 file

0.1.5

1 file

0.1.4

1 file

0.1.3

1 file

0.1.2

1 file

0.1.1

1 file

0.1.0

1 file

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page