| name | android-apk-patch |
| description | A complete, self-contained reference for modifying Android APKs — from decompilation to adding features, signing, testing, and local delivery. Covers split APKs, GApps, signing schemes, and dynamic analysis. No Xposed/LSPatch dependency. |
| metadata | {"author":"mte90","version":"1.0.0","tags":["android","apk","gapps","reveng","patch"]} |
Table of Contents
- Toolchain Setup & Version Requirements
- Decompilation & Recompilation Workflow
- Split APK Handling (.apks / .apkm / .xapk)
- Signing: Schemes, Pitfalls, and Fix Procedures
- Dependency Hell: Frameworks, AndroidX, OEM Resources
- GApps-Dependent APKs: Patching & Workarounds
- Testing Without a Physical Device
- Local Testing: Redroid & Waydroid
- Advanced Patching Techniques → references/advanced-patching.md
- Adding Features to an APK → references/adding-features.md
- Delivering the APK to an End User → references/delivery.md
- End-to-End Workflow: Zero to Delivery → references/end-to-end-workflow.md
- Troubleshooting: Common Failures & Resolutions → references/troubleshooting.md
- Quick Reference Cheat Sheet → references/quick-reference.md
1. Toolchain Setup & Version Requirements
1.1 Mandatory Tools (Current Stable Versions)
1.2 Why Old Tool Versions Will Fail You
- Apktool < 2.9.0 — Cannot handle API 34+ resources. Decompilation of Android 14+ APKs will crash with obscure AAPT errors or produce corrupted
resources.arsc output.
- Apktool < 2.6.0 — Uses legacy AAPT (v1) instead of AAPT2. Causes massive resource recompilation failures with any app using AndroidX, Material Components, or modern resource features.
- smali < 3.0.5 — Cannot disassemble DEX format 038+ (Android 13+). baksmali will fail with
"Unsupported DEX format" on any app compiled with minSdkVersion 33+.
- jarsigner (any version) — Only produces v1 (JAR) signatures. Modern Android (7.0+) requires at minimum v2 signatures. Using jarsigner will result in
INSTALL_PARSE_FAILED_NO_CERTIFICATES or silent signature rejection.
1.3 Environment Setup
java -version
export JAVA_HOME=/usr/lib/jvm/java-17-openjdk
chmod +x apktool
sudo mv apktool /usr/local/bin/
sdkmanager "build-tools;35.0.0"
sdkmanager "platform-tools"
pip install frida-tools
2. Decompilation & Recompilation Workflow
2.1 Standard Single APK Workflow
jadx-gui target.apk
jadx -d analysis_output/ target.apk
apktool d target.apk -o decompiled/ -f
apktool b decompiled/ -o rebuilt.apk
zipalign -v -p 4 rebuilt.apk rebuilt_aligned.apk
apksigner sign --ks ~/.android/debug.keystore \
--ks-pass pass:android \
--key-pass pass:android \
rebuilt_aligned.apk
apksigner verify --verbose --print-certs rebuilt_aligned.apk
adb install -r rebuilt_aligned.apk
2.2 Important Decompilation Flags
apktool d target.apk -o out/ -f --api 35
apktool d target.apk -o out/ -f --ignore-missing-resources
apktool d target.apk -o out/ -f -s
apktool d target.apk -o out/ -f -r
apktool d framework.jar -api 29 -o out/
apktool if /path/to/oem-framework.apk
apktool d system_app.apk -o out/ -f
2.3 Understanding the Decompiled Structure
decompiled/
├── AndroidManifest.xml # App manifest (XML format, editable)
├── apktool.yml # Apktool metadata (DO NOT manually edit version fields)
├── assets/ # Raw asset files
├── lib/ # Native libraries (.so files) by architecture
│ ├── arm64-v8a/
│ ├── armeabi-v7a/
│ └── x86_64/
├── original/ # Original files (META-INF, etc.)
├── res/ # Compiled resources (XML format, editable)
│ ├── values/ # String values, styles, colors, dimensions
│ ├── layout/ # Layout XML files
│ ├── drawable/ # Vector drawables, layer lists
│ ├── xml/ # Preferences, searchable config, etc.
│ └── mipmap-*/ # App icons by density
├── smali/ # Disassembled DEX bytecode (MAIN CODE)
│ ├── com/
│ │ └── example/
│ │ └── app/
│ │ ├── MainActivity.smali
│ │ └── ...
│ └── android/
│ └── ...
└── smali_classes2/ # Additional DEX files (multi-dex apps)
└── ...
2.4 Smali Editing Basics
Smali is a human-readable representation of DEX bytecode. Key patterns you'll encounter:
# Class declaration
.class public Lcom/example/app/MainActivity;
.super Landroidx/appcompat/app/AppCompatActivity;
# Method declaration
.method public onCreate(Landroid/os/Bundle;)V
.registers 4
# Calling super
invoke-super {p0, p1}, Landroidx/appcompat/app/AppCompatActivity;->onCreate(Landroid/os/Bundle;)V
# Setting a field
const-string v0, "patched_value"
iput-object v0, p0, Lcom/example/app/MainActivity;->mStatus:Ljava/lang/String;
# Conditional check
if-eqz v0, :cond_skip
# ... code ...
:cond_skip
return-void
.end method
# Common patches:
# 1. Force a boolean method to always return true
.method public isPremium()Z
.registers 2
const/4 v0, 0x1 # true
return v0
.end method
# 2. NOP out a method call (comment effect by replacing with nop)
# Replace: invoke-virtual {v0}, Lcom/example/Payroll;->show()V
# With: nop
# 3. Remove a permission check
# Replace: if-eqz v0, :cond_deny
# With: nop # always falls through
2.5 Resource Editing
Resources are stored as compiled XML (AXML) that apktool decodes. Common modifications:
<string name="app_name">My Patched App</string>
<color name="primary">#FF6200EE</color>
<uses-permission android:name="android.permission.QUERY_ALL_PACKAGES" />
<activity android:name=".HiddenActivity" android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<TextView
android:id="@+id/status_text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="PATCHED" />
3. Split APK Handling
3.1 Understanding Split APK Formats
Modern Play Store apps use Android App Bundles (AAB), which are split into multiple APKs:
| Format | Source | Extension | Contents |
|---|
| Bundletool | Google Play / developer tools | .apks | ZIP with base + splits + toc.pb (protobuf metadata) |
| APKMirror | APKMirror downloads | .apkm | Same as .apks, renamed |
| APKPure | APKPure downloads | .xapk | Similar concept, slightly different metadata |
| Individual | Manual extraction | .apk each | Each split as separate APK file |
Split types you'll encounter:
base.apk — Core app code, shared resources, manifest
split_config.arm64_v8a.apk — ARM64 native libraries
split_config.armeabi_v7a.apk — ARMv7 native libraries
split_config.x86_64.apk — x86_64 native libraries
split_config.xxhdpi.apk — Screen density-specific resources
split_config.en.apk — Language-specific resources
split_config.google_apis.apk — Google APIs integration code
split_config.firebase.apk — Firebase integration code
3.2 Extraction & Merging Workflow
mkdir splits/ && cd splits/
unzip ../input.apks
unzip ../input.xapk
java -jar APKEditor.jar m -i base.apk split_config.arm64_v8a.apk \
split_config.xxhdpi.apk split_config.en.apk -o merged.apk
apktool d merged.apk -o decompiled/ -f
apktool b decompiled/ -o modified_base.apk
zipalign -v -p 4 modified_base.apk modified_base_aligned.apk
apksigner sign --ks ~/.android/debug.keystore \
--ks-pass pass:android --key-pass pass:android \
modified_base_aligned.apk
3.3 Re-installing Modified Split APKs
Option A: Replace base APK only (recommended when possible)
If you only modified the base APK (most common scenario), you can keep the original split APKs:
adb uninstall com.example.app
adb install-multiple modified_base_aligned.apk \
splits/split_config.arm64_v8a.apk \
splits/split_config.xxhdpi.apk \
splits/split_config.en.apk
Option B: Re-sign ALL APKs with the same key
If splits fail because of signature mismatch (some splits embed signature checks):
for apk in modified_base_aligned.apk splits/split_config.*.apk; do
apksigner sign --ks ~/.android/debug.keystore \
--ks-pass pass:android --key-pass pass:android \
"$apk"
done
adb install-multiple modified_base_aligned.apk splits/split_config.*.apk
Option C: Create a new bundle for distribution
java -jar bundletool.jar build-bundle \
--modules=modified_base_aligned.apk,splits/split_config.arm64_v8a_apk \
--output=patched.apks
3.4 Split APK Pitfalls
| Pitfall | Description | Fix |
|---|
| Missing native libs after merge | APKEditor may not merge all lib/ directories from arch splits | Manually copy missing .so files into merged APK's lib/ before decompiling |
| Resource conflicts during merge | Two splits define same resource ID with different values | Merge only base + arch split first, then add density/language splits one at a time |
toc.pb metadata mismatch | Modified APK doesn't match original bundle metadata | Don't use bundletool for installation — use adb install-multiple directly |
| Feature module splits | Some splits contain code (not just resources) | Decompile the feature split separately, understand its code, and merge smali directories |
Base APK decompilation produces null resources | Known apktool issue with some split APK bases | Merge with APKEditor first, or use --ignore-missing-resources flag |
4. Signing: Schemes, Pitfalls, and Fix Procedures
4.1 APK Signature Schemes Explained
| Scheme | Since | Mechanism | Tamper Resistance |
|---|
| v1 (JAR signing) | API 1 (all Android) | Signs individual file entries inside META-INF/ | Weak — can strip and re-sign without app knowing |
| v2 (APK Signature Scheme v2) | API 24 (Android 7.0) | Binary signature block before ZIP central directory; verifies entire APK | Strong — any byte modification (even metadata) invalidates it |
| v3 (APK Signature Scheme v3) | API 28 (Android 9.0) | Same as v2 + supports key rotation via proof-of-rotation lineage | Same integrity + allows changing signing keys |
| v4 (APK Signature Scheme v4) | API 30 (Android 11) | Merkle hash tree stored separately; enables streaming verification during incremental installs | Same integrity + incremental install support |
Critical rule: v2/v3 are whole-APK signatures. Modifying even a single byte of smali code, a single resource string, or even the ZIP comment field invalidates them. This is why every recompilation requires a full re-sign.
Android verification order: v4 → v3 → v2 → v1 (falls back through each level).
4.2 apksigner vs jarsigner — Why jarsigner Is Dead
| Feature | apksigner | jarsigner |
|---|
| Signature schemes | v1, v2, v3, v4 | v1 only |
| Whole-APK verification | Yes | No |
| Key rotation support | Yes (v3) | No |
| Required for API 24+ | Yes | No — will be rejected |
| Alignment awareness | Yes | No |
| Source | Android SDK Build Tools | JDK |
NEVER use jarsigner for APK signing. Period. It produces v1-only signatures that modern Android (7.0+) silently rejects or explicitly denies installation.
4.3 Proper Signing Procedure
keytool -genkey -v -keystore ~/release.keystore \
-alias myapp \
-keyalg RSA \
-keysize 2048 \
-validity 10000
zipalign -v -p 4 rebuilt.apk rebuilt_aligned.apk
apksigner sign \
--ks ~/release.keystore \
--ks-key-alias myapp \
--ks-pass pass:your_keystore_password \
--key-pass pass:your_key_password \
--v1-signing-enabled true \
--v2-signing-enabled true \
--v3-signing-enabled true \
rebuilt_aligned.apk
apksigner verify --verbose --print-certs rebuilt_aligned.apk
apksigner verify --print-certs -v rebuilt_aligned.apk
4.4 Signature-Related Failures and Fixes
| Error Message | Root Cause | Fix |
|---|
INSTALL_FAILED_UPDATE_INCOMPATIBLE | App already installed with different signing key | adb uninstall com.example.app then reinstall |
INSTALL_PARSE_FAILED_NO_CERTIFICATES | APK not signed at all | Sign with apksigner |
INSTALL_FAILED_VERIFICATION_FAILURE | Device requires verified installs (MIUI, Samsung Knox) | Disable MIUI optimization, or enable "Install via USB" in Developer Options |
| App installs but immediately crashes | Runtime signature self-check (PackageManager.getPackageInfo() with GET_SIGNATURES) | See Section 9.3 (Frida signature bypass) or patch smali signature check |
SecurityException: Signature not valid | App explicitly checks caller signature | Patch the smali signature check, or use signature spoofing (see 6.4) |
INSTALL_FAILED_INVALID_APK | APK corrupted or malformed after recompilation | Check for AAPT2 errors during build, verify zip integrity |
| Play Store says "App not installed" | Custom signature not recognized by Play Store | Expected — Play Store only accepts Google-signed updates for apps it manages |
Splits fail with INSTALL_FAILED_SESSION_INVALID | Splits signed with different keys | Sign ALL splits with the same keystore before install-multiple |
4.5 Key Rotation with v3 Signing
If you need to change signing keys (e.g., you lost your keystore):
apksigner sign \
--ks new-release.keystore \
--ks-key-alias newkey \
--ks-pass pass:new_password \
--v1-signing-enabled true \
--v2-signing-enabled true \
--v3-signing-enabled true \
--lineage /path/to/lineage-file \
rebuilt_aligned.apk
apksigner sign \
--ks old-release.keystore \
--ks-key-alias oldkey \
--ks-pass pass:old_password \
--v3-signing-enabled true \
--lineage /path/to/lineage-file \
--next-signer new-release.keystore newkey pass:new_password \
dummy.apk
Caveat: Key rotation only works if the app was PREVIOUSLY signed with v3. If the original app only has v1/v2, you're out of luck — you must uninstall and reinstall.
5. Dependency Hell: Frameworks, AndroidX, OEM Resources
5.1 Common Resource Dependency Errors
# Error 1: Missing AndroidX resources
error: resource 'com.example:id/material_textinput' not found
# Error 2: Styleable attribute not found
error: style attribute 'attr/layout_constraintBaseline_toBaselineOf' not found
# Error 3: AAPT2 crash
ERROR: AAPT2 aapt2(...) exited with code 1
brut.androlib.AndrolibException
# Error 4: Duplicate resource
error: resource 'style/Theme.MaterialComponents' has already been defined
# Error 5: Missing framework resource
error: resource '@android:configui_configFlags' not found
5.2 Systematic Dependency Resolution
Step 1: Install ROM-specific frameworks
For most third-party apps, Apktool's bundled framework (since 2.5.0) is sufficient. But for system apps, OEM apps, or apps that reference framework resources not in AOSP, you need:
apktool if framework-res.apk
adb pull /system/framework/ ./device_frameworks/
apktool if device_frameworks/sec_platform_library.jar
apktool if device_frameworks/com.samsung.device.jar
apktool if device_frameworks/frameworksSamsungSDK.jar
apktool if device_frameworks/framework-miui-res.apk
apktool if device_frameworks/MiuiSdk.jar
apktool if device_frameworks/framework-mi.jar
apktool if device_frameworks/HWExtension.jar
apktool if device_frameworks/hwsdk.jar
apktool if device_frameworks/oplus-framework.jar
apktool if device_frameworks/oplus-res.apk
ls ~/.local/share/apktool/framework/
Step 2: Install shared library APKs
Some apps depend on vendor-shared library APKs:
adb shell pm list packages -f | grep shared
adb shell pm list packages -f | grep library
adb pull /system/app/SomeSharedLib/SomeSharedLib.apk
apktool if SomeSharedLib.apk
Step 3: Force API level to resolve resource conflicts
apktool d target.apk -o out/ -f --api 35
apktool d target.apk -o out/ -f --api 29
Step 4: Use the ignore flag for known non-critical missing resources
apktool d target.apk -o out/ -f --ignore-missing-resources
5.3 AndroidX-Specific Issues
Issue A: Cross-library resource references
AndroidX migrated all android.support.* classes to androidx.* namespaces. This causes two major categories of issues:
- Cross-library resource references: AndroidX libraries reference resources across package boundaries. When decompiled, these may resolve incorrectly.
- R8-obfuscated resource names: When R8 shrinks an app, it may remove unused AndroidX resources. On recompilation, the remaining code may reference resources that were stripped.
Fix: Ensure Apktool 2.11.0+ is used (correctly handles AndroidX cross-references). For R8-obfuscated apps, manually add the required library .aar files as frameworks.
Issue B: Obfuscation
R8/ProGuard/DexGuard can strip resources. This is extremely hard to resolve automatically—your best options are:
- Decompile the original APK without modification and immediately recompile (if that fails too, it's a tool limitation)
- Manually add the missing resource definition to
res/values/ files
- Use
--ignore-missing-resources and accept that some features may break
5.4 AAPT2 Error Catalog
| Error | Cause | Fix |
|---|
resource has already been defined | Duplicate resource entry in AAPT2 | Apktool 2.11.0+ handles most cases. If persists, search for duplicate definitions in res/values/*.xml |
failed to compile values files | Style/attr references to undefined resources | Check attrs.xml, styles.xml, themes.xml for references to removed resources |
malformed compiled jar | Samsung DEX format 039 in framework | apktool d -api 29 |
no resource identifier found for attribute | Missing library dependency | Install the required library APK as framework |
invalid file path | Windows path length limit or special characters in resources | Move project to short path, rename files |
unmarshalling resource table | Corrupted resources.arsc in original APK | Try --ignore-missing-resources, or use APKEditor to decompile instead |
Error: java.nio.BufferOverflowException | Very large resource table (>2GB) | Use 64-bit Java and increase heap: JAVA_OPTS="-Xmx4g" apktool b ... |
6. GApps-Dependent APKs: Patching & Workarounds
6.1 The GApps Dependency Problem
Apps that depend on Google Play Services are the hardest to patch because:
- Split APK hell: Play Services is distributed as ~100+ dynamic feature modules via split APKs
- Runtime signature verification: Both the OS and other GApps verify Play Services' signature at runtime
- DroidGuard attestation: Google's attestation service is deeply integrated into Play Services core
- Automatic updates: The Play Store auto-updates Play Services, overwriting any patches
- Play Integrity API: Modern apps use Play Integrity to verify device and app integrity — impossible to fully bypass on emulators
Bottom line: Do NOT try to directly patch com.google.android.gms. It's a losing battle. Instead, use the approaches in the following sections.
6.2 Strategy 1: MicroG (Recommended for GApps-Free Patching)
MicroG is a lightweight, open-source replacement for Google Play Services.
6.3 Strategy 2: Patching Individual GApps (YouTube, Maps, etc.)
ReVanced (the successor to Vanced) provides pre-made patches:
Manual patching of GApps:
The key challenge: GApps perform runtime signature verification. They check their own signature against a known Google certificate fingerprint and refuse to function if the signature doesn't match.
apktool d google_app.apk -o decompiled/
6.4 Strategy 3: Signature Spoofing
What it does: Makes MicroG's signature appear as the real Google Play Services signature to other apps.
ROM-level support (cleanest approach):
- LineageOS: Settings → System → Developer options → "Allow signature spoofing"
- AXP.OS, /e/OS, CalyxOS: Built-in with per-app control
Magisk module approach (for stock ROMs):
- Patches
framework.jar at runtime to allow apps to declare spoofing support
- MicroG declares spoofing support in its manifest
- Other apps query MicroG's signature and receive Google's signature bytes
6.5 Strategy 4: Play Integrity Bypass (2025 State)
| Tool | Type | Works On | Status |
|---|
| TrickyStore | Magisk module | Android 8+ (with Zygisk) | Active — spoofs device attestation key |
| Play Integrity Fix NEXT | Magisk module | Android 8+ (with Zygisk) | Active — aims for valid attestation |
| PIFork | Magisk module | Pre-Android 13 | Active fork by osm0sis |
Stack for passing Play Integrity (2025):
- Root via Magisk / KernelSU / APatch
- Install ZygiskNext
- Install Play Integrity Fix (or NEXT/fork)
- Install TrickyStore + TrickyStore Addon
- Do NOT put
com.google.android.gms on Magisk DenyList
CRITICAL LIMITATION: Play Integrity does NOT pass on any emulator. Emulators are detected via hardware attestation checks that cannot be spoofed in software. For Play Integrity testing, you MUST use a physical device.
7. Testing Without a Physical Device
7.1 Emulator Selection Matrix
| Emulator | Root Support | ARM Translation | GApps Support | Best For | Verdict |
|---|
| Android Studio AVD | Magisk via rootAVD | Native (ARM images) / Translated (x86) | Google APIs / Google Play images | Dev, security testing, patch verification | Best choice |
| Genymotion | Built-in basic root | libhoudini (not included by default since 3.x) | Requires manual GApps flash | Professional CI/CD testing | Good for automated testing |
| Waydroid | Magisk via script | libndk_translation (SSE 4.2 required) | Manual via waydroid_script | Linux desktop Android apps | Good for Linux users |
| BlueStacks | Difficult, no Magisk | Built-in | Pre-installed | Gaming only | Not recommended for dev work |
| Nox | Clunky, unreliable | Built-in | Partial | Gaming/automation | Not recommended for dev work |
7.2 Setting Up Android Studio AVD for APK Testing
pip3 install rootAVD
rootAVD -s test_device_api35
emulator -avd test_device_api35 -no-snapshot-load
adb shell su -c "id"
adb push module.zip /sdcard/Download/
adb install -r patched.apk
adb install-multiple base.apk split_config.arm64_v8a.apk split_config.en.apk
adb install -r -d -t patched.apk
adb uninstall com.example.app
adb install patched.apk
7.3 GApps Testing on Emulator
Option A: AVD with Google APIs image
Option B: AVD without Google APIs + MicroG (recommended for GApps-free testing)
adb install GmsCore.apk
adb install ntfy.apk
adb install patched.apk
7.4 x86 vs ARM Translation Issues
Android Studio AVD:
- x86_64 images: ~10x faster than ARM emulation. Uses native x86 execution.
- ARM64 images: Slower but runs ARM native libraries directly.
- ARM translation: When running x86_64 image with ARM APKs, the emulator translates ARM instructions to x86 transparently.
- Problem: Some native libraries (anti-tamper, anti-debug, DRM) detect the translation layer and refuse to run.
- Fix: Use ARM64 system images when testing apps with ARM-specific native code that performs translation detection.
Genymotion:
- Runs x86 images by default
- libhoudini for ARM translation (not included since Genymotion 3.x)
- Install via: https://github.com/vnsh01/Nyx (for Android 11)
- macOS Apple Silicon devices don't need translation (native ARM64)
Waydroid:
7.5 Frida Setup on Emulator
pip install frida-tools
adb push frida-server /data/local/tmp/
adb shell "chmod 755 /data/local/tmp/frida-server"
adb shell "/data/local/tmp/frida-server -D &"
frida-ps -U
frida -U -n com.example.app
frida -U -f com.example.app -l hook.js --no-pause
frida -U -l bypass_signature.js -n com.example.app
7.6 ADB Commands Reference for Testing
adb install -r app.apk
adb install -d app.apk
adb install -t app.apk
adb install-multiple base.apk s1.apk s2.apk
adb uninstall com.example.app
adb logcat
adb logcat -s "ActivityManager"
adb logcat | grep -i "FATAL"
adb shell dumpsys package com.example.app
adb push file.txt /sdcard/
adb pull /sdcard/file.txt ./
adb shell su -c "command"
adb shell su -c "mount -o rw,remount /system"
adb push patched.apk /system/app/TargetApp/target.apk
adb shell su -c "chmod 644 /system/app/TargetApp/target.apk"
emulator -avd NAME -no-snapshot-load
adb reboot
7.7 ADB Install Flags Reference
| Flag | Meaning | Use Case |
|---|
-r | Replace/reinstall existing app | Updating a patched APK |
-d | Allow version downgrade | Installing older patched version |
-t | Allow test APKs (android:testOnly="true") | Debug/test builds |
-g | Grant all runtime permissions | Testing with all permissions |
--force-install | Force install (Android 13+) | Bypass signature/version checks |
-p | Partial install (split APK mode) | Internal use for install-multiple |
8. Local Testing: Redroid & Waydroid
8.1 Signing for Local Testing: Everything You Need to Know
8.1.1 The Debug Keystore (Quick & Dirty)
Location: ~/.android/debug.keystore
Password: android
Alias: androiddebugkey
Key pass: android
When to use: Local-only testing on redroid, Waydroid, your own devices, emulators.
When NOT to use: Distributing APKs to others, publishing to Play Store.
8.1.2 Custom Keystore for Reproducible Signing
keytool -genkey -v \
-keystore ~/apk-testing.keystore \
-alias testing \
-keyalg RSA \
-keysize 2048 \
-validity 10000
8.1.3 The Critical Order: zipalign THEN sign
zipalign -v -p 4 rebuilt.apk rebuilt_aligned.apk
apksigner sign --ks key.keystore aligned.apk
apksigner sign --ks key.keystore rebuilt.apk
zipalign -v -p 4 rebuilt.apk aligned.apk
Why: v2/v3 signatures are computed over the entire APK binary, including the position of entries within the ZIP. zipalign moves entries around to 4-byte boundaries, which invalidates any signature computed before alignment.
8.1.4 Signing Split APKs with the Same Key
When testing split APKs on redroid/Waydroid, ALL splits in a bundle must share the same signing key:
unzip input.apks -d splits/
for apk in splits/*.apk; do
zipalign -v -p 4 "$apk" "${apk%.apk}_aligned.apk"
done
for apk in splits/*_aligned.apk; do
apksigner sign \
--ks ~/apk-testing.keystore \
--ks-key-alias testing \
--ks-pass pass:your_password \
--key-pass pass:your_password \
"$apk"
done
adb install-multiple splits/base_aligned.apk \
splits/split_config.arm64_v8a_aligned.apk \
splits/split_config.en_aligned.apk
8.1.5 uber-apk-signer: All-in-One Alternative
If you want to skip the manual zipalign + apksigner dance:
java -jar uber-apk-signer.jar --apks rebuilt.apk
java -jar uber-apk-signer.jar \
--apks rebuilt.apk \
--ks ~/apk-testing.keystore \
--ksAlias testing \
--ksPass your_password
java -jar uber-apk-signer.jar --apks splits/
8.1.6 Common Signing Mistakes & Symptoms
| Mistake | What Happens | Fix |
|---|
| zipalign AFTER sign | INSTALL_FAILED_VERIFICATION | Always zipalign first |
| Used jarsigner instead of apksigner | v2/v3 signatures missing; Android 7.0+ rejects | Use apksigner |
| Split APKs signed with different keys | INSTALL_FAILED_SESSION_INVALID | Sign ALL with same keystore |
| Modified APK after signing | Signature broken | Never modify post-sign |
| Wrong keystore password | Keystore was tampered with, or password was incorrect | Verify with keytool -list |
| Not aligned at all | App installs but runs slow; INSTALL_PARSE_FAILED_UNEXPECTED_EXCEPTION on some ROMs | Always run zipalign -v -p 4 |
| Missing v2 scheme on API 24+ | Silent rejection or INSTALL_PARSE_FAILED_NO_CERTIFICATES | apksigner defaults to v1+v2; verify with apksigner verify -v |
Best Practices
- Always backup the original APK: Keep an unmodified copy before any patching—recovery is impossible without it.
- Test on multiple Android versions: Verify compatibility across API levels (e.g., 29, 33, 35) before distribution.
- Use proper signing keys (don't lose them): Store keystore files securely with strong passwords; losing your key means you can never update the app.
- Keep the original package name unless deliberately changing it: Changing package name creates a new app identity, preventing updates.
- Verify split APK alignment: Use
zipalign -v -p 4 on all splits before signing; misalignment causes runtime crashes.
- Test GApps-dependent APKs in an emulator first: Use Android Studio AVD or redroid to catch GApps integration issues before physical device testing.
- Document all modifications made: Keep a changelog of smali/resource edits for debugging and future patches.
8.2 Redroid: Android in Docker for Headless APK Testing
8.2.1 What Is Redroid?
Redroid (Remote-android) runs a full Android system inside a Docker container. It does NOT use QEMU or virtualization — it runs Android userspace natively on the host Linux kernel using LXC primitives.
Key characteristics:
- Runs as a Docker container — fully containerized, reproducible, scriptable
- Root by default (uid=0 inside the container)
- ADB access via TCP on port 5555
- No display by default (headless) — use scrcpy for screen mirroring if needed
- Supports Android 11 through 15
- Perfect for CI/CD, batch testing, and automated APK validation
Official resources:
8.2.2 Prerequisites & Kernel Setup
sudo apt install docker.io docker-compose
sudo usermod -aG docker $USER
sudo apt install linux-modules-extra-$(uname -r)
sudo modprobe binder_linux
ls /dev/binderfs 2>/dev/null && echo "OK: binderfs found" || echo "MISSING"
sudo modprobe ashmem_linux
Kernel version requirement: 4.14+ (virtually all modern distros satisfy this).
WSL2 support: Possible but requires a custom WSL2 kernel with binder modules. See https://github.com/remote-android/redroid-doc/blob/master/deploy/wsl.md. macOS is NOT directly supported (Docker Desktop on macOS lacks binder).
8.2.3 Basic Docker Setup
Minimal headless container (for pure APK install/run testing):
docker run -d \
--privileged \
--name redroid14 \
-p 5555:5555 \
redroid/redroid:14.0.0-latest
With GPU acceleration (Intel/AMD via Mesa/virgl):
docker run -d \
--privileged \
--name redroid14 \
-p 5555:5555 \
-v /dev/dri:/dev/dri \
redroid/redroid:14.0.0-latest \
androidboot.redroid_width=1080 \
androidboot.redroid_height=1920 \
androidboot.redroid_dpi=320
With persistent data (apps survive container restart):
docker volume create redroid_data
docker run -d \
--privileged \
--name redroid14 \
-p 5555:5555 \
-v /dev/dri:/dev/dri \
-v redroid_data:/data \
redroid/redroid:14.0.0-latest \
androidboot.redroid_width=1080 \
androidboot.redroid_height=1920 \
androidboot.redroid_dpi=320
Docker Compose (recommended for reproducible setups):
services:
redroid:
image: redroid/redroid:14.0.0-latest
container_name: redroid14
privileged: true
ports:
- "5555:5555"
volumes:
- /dev/dri:/dev/dri
- redroid_data:/data
command:
- "androidboot.redroid_width=1080"
- "androidboot.redroid_height=1920"
- "androidboot.redroid_dpi=320"
volumes:
redroid_data:
docker compose up -d
docker compose logs -f
docker compose down
8.2.4 Available Docker Image Tags
| Tag | Android | Notes |
|---|
redroid/redroid:11.0.0-latest | 11 | Stable, legacy |
redroid/redroid:12.0.0-latest | 12 | Stable |
redroid/redroid:13.0.0-latest | 13 | Stable |
redroid/redroid:14.0.0-latest | 14 | Recommended — most stable |
redroid/redroid:15.0.0-latest | 15 | Newer, check stability |
redroid/redroid:14.0.0_64only-latest | 14 | 64-bit only (smaller, fewer compat issues) |
erstt/redroid | Various | Pre-built with ARM translation |
8.2.5 ADB Connection & APK Installation
docker logs redroid14 2>&1 | tail -5
adb connect localhost:5555
adb devices
adb -s localhost:5555 install rebuilt_aligned.apk
adb -s localhost:5555 install -r rebuilt_aligned.apk
adb -s localhost:5555 install -d -t --force-install rebuilt_aligned.apk
adb -s localhost:5555 install-multiple base_aligned.apk \
split_config.arm64_v8a_aligned.apk \
split_config.xxhdpi_aligned.apk
adb -s localhost:5555 install -r -g rebuilt_aligned.apk
adb -s localhost:5555 shell pm list packages
adb -s localhost:5555 shell pm list packages -3
adb -s localhost:5555 uninstall com.example.app
adb -s localhost:5555 shell am force-stop com.example.app
adb -s localhost:5555 shell am start -n com.example.app/.MainActivity
adb -s localhost:5555 shell
whoami
id
scrcpy -s localhost:5555
8.2.6 GApps, Magisk & ARM Translation on Redroid
All three can be installed via redroid-script without rebuilding the Docker image:
git clone https://github.com/abing7k/redroid-script.git
cd redroid-script
sudo python3 redroid-script.py redroid14 --gapps --magisk --libndk
sudo python3 redroid-script.py redroid14 --gapps
sudo python3 redroid-script.py redroid14 --magisk
sudo python3 redroid-script.py redroid14 --libndk
sudo python3 redroid-script.py redroid14 --houdini
docker restart redroid14
Note: Magisk installation may require 2-3 container restarts to fully initialize, especially with Zygisk enabled.
8.2.7 Complete Redroid APK Testing Workflow
sudo modprobe binder_linux
docker volume create redroid_data
docker run -d --privileged --name redroid14 -p 5555:5555 \
-v redroid_data:/data redroid/redroid:14.0.0-latest
adb connect localhost:5555
git clone https://github.com/abing7k/redroid-script.git
sudo python3 redroid-script.py redroid14 --gapps --libndk
docker restart redroid14
sleep 30
adb connect localhost:5555
apktool d target.apk -o dec/ -f
apktool b dec/ -o rebuilt.apk
zipalign -v -p 4 rebuilt.apk rebuilt_aligned.apk
apksigner sign --ks ~/apk-testing.keystore \
--ks-key-alias testing --ks-pass pass:your_password \
--key-pass pass:your_password rebuilt_aligned.apk
adb -s localhost:5555 uninstall com.example.app 2>/dev/null
adb -s localhost:5555 install rebuilt_aligned.apk
adb -s localhost:5555 shell am start -n com.example.app/.MainActivity
adb -s localhost:5555 logcat -s "AndroidRuntime:E" "*:S"
scrcpy -s localhost:5555
docker stop redroid14
docker rm redroid14
docker volume rm redroid_data
8.2.8 Redroid Known Issues
| Issue | Workaround |
|---|
| Container won't start (binder missing) | sudo modprobe binder_linux + linux-modules-extra-$(uname -r) |
| Apps can't access internet | Use --net=host in docker run |
| Long first boot on WSL2 | Store data as Docker volume (not bind mount) |
| ARM app crashes on x86 host | Install ARM translation: redroid-script.py <id> --libndk |
| No audio output | Expected in headless mode; use scrcpy for visual only |
| Magisk instability after install | Restart container 2-3 times |
| GPU artifacts on NVIDIA | Use software rendering (remove -v /dev/dri:/dev/dri) |
8.3 Waydroid: Android Container with Desktop UI
8.3.1 What Is Waydroid?
Waydroid runs Android as an LXC container with native desktop window integration. Unlike redroid, it displays Android apps as regular windows on your Linux desktop. It requires a Wayland compositor (GNOME Wayland, KDE Plasma Wayland, Sway, Hyprland, etc.).
Key characteristics:
- Android apps appear as native desktop windows
- Clipboard sync between Linux and Android (Wayland only)
- Audio via PulseAudio/PipeWire
- Requires Wayland compositor (or nested weston for X11)
- ADB access via local socket
- Single Android session (not multi-container like redroid)
Official resources:
8.3.2 Installation
sudo apt install curl ca-certificates
curl https://repo.waydro.id | sudo bash
sudo apt install waydroid
curl https://repo.waydro.id | sudo bash
sudo apt install waydroid
sudo pacman -S waydroid
sudo dnf install waydroid
sudo waydroid init
sudo waydroid init -s GAPS
waydroid session start
waydroid show-full-ui
8.3.3 GApps, Magisk & ARM Translation via waydroid_script
casualsnek/waydroid_script is the Swiss army knife for Waydroid:
git clone https://github.com/casualsnek/waydroid_script.git
cd waydroid_script
sudo pip3 install requests pyyaml
sudo python3 waydroid_script.py -g -m -l
sudo python3 waydroid_script.py -g
sudo python3 waydroid_script.py -g --variant pico
sudo python3 waydroid_script.py -g --microg
sudo python3 waydroid_script.py -m
sudo python3 waydroid_script.py -l
sudo python3 waydroid_script.py -L
waydroid session stop
sudo systemctl restart waydroid-container
waydroid session start && waydroid show-full-ui
ARM translation critical requirement: Your CPU must support SSE 4.2 instruction set:
cat /proc/cpuinfo | grep sse4_2
8.3.4 ADB Connection & APK Installation
adb devices
waydroid app install /path/to/rebuilt_aligned.apk
adb install /path/to/rebuilt_aligned.apk
adb install -r /path/to/rebuilt_aligned.apk
adb install -d -t --force-install /path/to/app.apk
adb install-multiple base.apk split_config.arm64_v8a.apk
waydroid app list
waydroid app launch com.example.app
waydroid app remove com.example.app
waydroid app list --show-package-name
adb shell
sudo waydroid shell
waydroid ctrl +p
8.3.5 Complete Waydroid APK Testing Workflow
curl https://repo.waydro.id | sudo bash
sudo apt install waydroid
sudo waydroid init
git clone https://github.com/casualsnek/waydroid_script.git
cd waydroid_script
sudo python3 waydroid_script.py -g -m -l
cd ..
waydroid session stop && sudo systemctl restart waydroid-container
waydroid session start && waydroid show-full-ui
apktool d target.apk -o dec/ -f
apktool b dec/ -o rebuilt.apk
zipalign -v -p 4 rebuilt.apk rebuilt_aligned.apk
apksigner sign --ks ~/apk-testing.keystore \
--ks-key-alias testing --ks-pass pass:your_password \
--key-pass pass:your_password rebuilt_aligned.apk
waydroid app remove com.example.app 2>/dev/null
waydroid app install rebuilt_aligned.apk
waydroid app launch com.example.app
adb logcat -s "AndroidRuntime:E" "*:S"
8.3.6 NVIDIA GPU: Known Pain Point
Waydroid relies on Mesa/GBM for GPU rendering. NVIDIA's proprietary driver doesn't use Mesa:
WAYDROID_DISABLE_GPU=1 waydroid session start
8.3.7 Waydroid Known Issues
| Issue | Workaround |
|---|
| NVIDIA GPU: no/flickering rendering | WAYDROID_DISABLE_GPU=1 or switch to iGPU |
| X11: no display | Run nested Wayland: weston & then WAYLAND_DISPLAY=wayland-1 waydroid show-full-ui |
| Camera not working | Very limited support (v4l2 only, no PipeWire) |
| Magisk flaky install | Check waydroid_script version; may need manual Magisk flash |
| Bluetooth/USB not working | Not natively supported |
| Dual GPU flickering | Force iGPU only or use software rendering |
| SELinux blocks Waydroid | Set permissive: sudo setenforce 0 (temp) or configure policy |
9. Advanced Patching Techniques
→ Full reference: references/advanced-patching.md
This section covers:
- Dynamic instrumentation with Frida (SSL pinning bypass, root detection bypass, signature bypass)
- Dealing with obfuscation (R8/ProGuard/DexGuard)
- Bypassing app integrity checks (file hash verification, tamper detection, DEX integrity, Play Integrity)
- Creating Magisk modules as an alternative to direct patching.
10. Adding Features to an APK
→ Full reference: references/adding-features.md
This section covers:
- Creating new smali classes from scratch
- Adding a new Activity to the app
- Adding external libraries (.jar / .aar)
- Injecting code into the app lifecycle
- Handling multi-DEX when adding code
- Practical example: Adding a "Debug Panel" to any app.
11. Delivering the APK to an End User
→ Full reference: references/delivery.md
This section covers:
- Release signing (not debug!)
- What the end user must do before installing
- Distribution methods (direct APK, .apks bundle, .apkm, ADB install)
- Preventing Play Store overwrites
- Preparing a user-friendly delivery package.
12. End-to-End Workflow: Zero to Delivery
→ Full reference: references/end-to-end-workflow.md
A complete checklist-driven workflow from toolchain setup to final delivery:
- Phase 1: Setup (one-time)
- Phase 2: Analysis
- Phase 3: Decompile
- Phase 4: Modify
- Phase 5: Recompile & Fix
- Phase 6: Sign & Test (Local)
- Phase 7: Prepare for Delivery
- Phase 8: Deliver
- Phase 9: Maintenance.
13. Troubleshooting: Common Failures & Solutions
→ Full reference: references/troubleshooting.md
Quick reference tables for:
- Decompilation failures
- Recompilation failures
- Installation failures
- Runtime failures (app crashes after install)
- Performance issues.
14. Quick Reference Cheat Sheet
→ Full reference: references/quick-reference.md
Includes:
- Command summary (decompile, recompile, align, sign, install)
- Smali quick reference
- Key file paths
- Common patterns cheat sheet.
References