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 Docs License Code Quality

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

Full documentation: apkfile.readthedocs.io

Install

pip install -U apkfile
# or
uv add apkfile

Usage

from apkfile import ApkFile, XapkFile, ApkmFile, ApksFile, ApkvFile

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

# Get apkv info (VInstall's format — https://github.com/vinstall/apkv-spec); optionally encrypted
apkv = ApkvFile(
    "/home/david/Downloads/backup.apkv", password="hunter2"
)  # password=None if unencrypted
print(apkv.app_name, apkv.exported_at)

# XAPK-bundled OBB expansion files, and pushing a standalone OBB alongside a plain apk ("apk + obb")
for obb in xapk.obb_files:
    print(obb.name, obb.is_patch, obb.size)
xapk.install()  # bundled OBBs are pushed to /sdcard/Android/obb/<package>/ automatically
apk.install(obb_paths=["main.1.com.example.game.obb"])

# Installing to every connected device happens in parallel, not one at a time
apk.install(grant_permissions=True, allow_downgrade=True)  # pm install -g -d

# Uninstall (same multi-device behavior as install: every connected device, in parallel)
apk.uninstall()
from apkfile import uninstall_apks

uninstall_apks("com.example.app", keep_data=True)

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
apkfile uninstall com.example.app # uninstall from connected device(s)
apkfile uninstall app.apk         # ...or by apk/bundle path, reading its package name

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.3.0.tar.gz (47.4 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.3.0-py3-none-any.whl (57.9 kB view details)

Uploaded Python 3

File details

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

File metadata

  • Download URL: apkfile-1.3.0.tar.gz
  • Upload date:
  • Size: 47.4 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: uv/0.12.5 {"installer":{"name":"uv","version":"0.12.5","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.3.0.tar.gz
Algorithm Hash digest
SHA256 d60a58b97a0009550cb0fac8f02f489432af1e4cd9c308a050a3aa7b2f24d0fd
MD5 e3661902ca2fe84a83982fa3d3b14922
BLAKE2b-256 86ffd39de7edb78e994793eb236b598bf95a931e9f5aa3dc62fde3fbe04df3d9

See more details on using hashes here.

File details

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

File metadata

  • Download URL: apkfile-1.3.0-py3-none-any.whl
  • Upload date:
  • Size: 57.9 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: uv/0.12.5 {"installer":{"name":"uv","version":"0.12.5","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.3.0-py3-none-any.whl
Algorithm Hash digest
SHA256 dc25d1a8b3e7df99836dc68666ada87782718322a85c4bf6131da506f232cebf
MD5 7644abec4b700b4fe4e38d7baddbd89e
BLAKE2b-256 c6d72b044d8b8a84dfae6c68a955fe018128000962612c36f7f14b5f659d5328

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

This release

1.3.0 This release

2 files

1.2.0

2 files

1.1.0

2 files

1.0.1

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