10 added, 0 removed. Audit B to B.
---
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
1. [Toolchain Setup & Version Requirements](#1-toolchain-setup--version-requirements)
2. [Decompilation & Recompilation Workflow](#2-decompilation--recompilation-workflow)
3. [Split APK Handling (.apks / .apkm / .xapk)](#3-split-apk-handling)
4. [Signing: Schemes, Pitfalls, and Fix Procedures](#4-signing-schemes-pitfalls-and-fix-procedures)
5. [Dependency Hell: Frameworks, AndroidX, OEM Resources](#5-dependency-hell-frameworks-androidx-oem-resources)
6. [GApps-Dependent APKs: Patching & Workarounds](#6-gapps-dependent-apks-patching--workarounds)
7. [Testing Without a Physical Device](#7-testing-without-a-physical-device)
8. [Local Testing: Redroid & Waydroid](#8-local-testing-redroid--waydroid)
9. [Advanced Patching Techniques](#9-advanced-patching-techniques) → [references/advanced-patching.md](references/advanced-patching.md)
10. [Adding Features to an APK](#10-adding-features-to-an-apk) → [references/adding-features.md](references/adding-features.md)
11. [Delivering the APK to an End User](#11-delivering-the-apk-to-an-end-user) → [references/delivery.md](references/delivery.md)
12. [End-to-End Workflow: Zero to Delivery](#12-end-to-end-workflow-zero-to-delivery) → [references/end-to-end-workflow.md](references/end-to-end-workflow.md)
13. [Troubleshooting: Common Failures & Resolutions](#13-troubleshooting-common-failures--resolutions) → [references/troubleshooting.md](references/troubleshooting.md)
14. [Quick Reference Cheat Sheet](#14-quick-reference-cheat-sheet) → [references/quick-reference.md](references/quick-reference.md)
---
## 1. Toolchain Setup & Version Requirements
### 1.1 Mandatory Tools (Current Stable Versions)
| Tool | Version | Purpose | URL |
|---|---|---|---|
| **Apktool** | **v2.11.0** (Jan 2025) | Decompile/recompile APKs | https://github.com/iBotPeaches/Apktool |
| **jadx** | **v1.5.1** | Read-only Java decompiler | https://github.com/skylot/jadx |
| **smali/baksmali** | **v3.0.9** (bundled in Apktool 2.11.0) | DEX bytecode assembly/disassembly | https://github.com/baksmali/smali |
| **APKEditor** | Latest | Merge/split APK handling | https://github.com/pxb1988/APKEditor |
| **apksigner** | SDK Build Tools **35.0.x** | APK signing (v1/v2/v3/v4) | Part of Android SDK |
| **zipalign** | SDK Build Tools **35.0.x** | APK alignment before signing | Part of Android SDK |
| **Frida** | **v17.9.0** | Dynamic instrumentation | https://frida.re |
| **ADB** | Platform Tools **35.0.x** | Device/emulator communication | Part of Android SDK |
### 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
```bash
# Java 11+ required (Java 17 recommended)
java -version # verify
export JAVA_HOME=/usr/lib/jvm/java-17-openjdk
# Install Apktool
# Download latest from https://github.com/iBotPeaches/Apktool/releases
# Move apktool.jar to PATH
chmod +x apktool
sudo mv apktool /usr/local/bin/
# Install Android SDK Build Tools (for apksigner + zipalign)
sdkmanager "build-tools;35.0.0"
sdkmanager "platform-tools"
# Install jadx
# Download from https://github.com/skylot/jadx/releases
# Extract and add bin/ to PATH
# Install Frida
pip install frida-tools
# Install APKEditor
# Download APKEditor.jar from https://github.com/pxb1988/APKEditor/releases
```
---
## 2. Decompilation & Recompilation Workflow
### 2.1 Standard Single APK Workflow
```bash
# Step 1: Analyze with jadx first (understand what you're modifying)
jadx-gui target.apk # GUI for exploration
# or
jadx -d analysis_output/ target.apk # CLI
# Step 2: Decompile with apktool
apktool d target.apk -o decompiled/ -f
# -f = force overwrite existing output directory
# --api 35 = force target API level (use if resource errors occur)
# Step 3: Make modifications
# - Edit smali files in decompiled/smali*/ (Java bytecode)
# - Edit resources in decompiled/res/ (layouts, strings, drawables)
# - Edit AndroidManifest.xml (permissions, components)
# - Edit assets/ (bundled files, web content)
# Step 4: Recompile
apktool b decompiled/ -o rebuilt.apk
# Use --use-aapt2 flag if not on 2.11.0 (now default)
# Step 5: Align (CRITICAL - must be BEFORE signing)
zipalign -v -p 4 rebuilt.apk rebuilt_aligned.apk
# Step 6: Sign
apksigner sign --ks ~/.android/debug.keystore \
--ks-pass pass:android \
--key-pass pass:android \
rebuilt_aligned.apk
# Step 7: Verify
apksigner verify --verbose --print-certs rebuilt_aligned.apk
# Step 8: Install
adb install -r rebuilt_aligned.apk
```
### 2.2 Important Decompilation Flags
```bash
# Force specific API level (fixes many resource resolution issues)
apktool d target.apk -o out/ -f --api 35
# Ignore missing resources (new in 2.11.x, for XAPK/base APK decompilation)
apktool d target.apk -o out/ -f --ignore-missing-resources
# Keep original sources only (skip resources, faster)
apktool d target.apk -o out/ -f -s
# Decode only resources (skip smali, for resource-only mods)
apktool d target.apk -o out/ -f -r
# For Samsung/Android 15 framework JARs with DEX format 039
apktool d framework.jar -api 29 -o out/
# Use a custom framework file (for OEM system APKs)
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:
```smali
# 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:
```xml
<!-- res/values/strings.xml - Change app name -->
<string name="app_name">My Patched App</string>
<!-- res/values/colors.xml - Change theme colors -->
<color name="primary">#FF6200EE</color>
<!-- AndroidManifest.xml - Add permissions -->
<uses-permission android:name="android.permission.QUERY_ALL_PACKAGES" />
<!-- AndroidManifest.xml - Export an activity (make it launchable) -->
<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>
<!-- res/layout/activity_main.xml - Modify UI -->
<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
```bash
# Step 1: Extract splits from bundle format
# .apks and .apkm are just ZIP files
mkdir splits/ && cd splits/
unzip ../input.apks # or .apkm
# This produces: base.apk, split_config.*.apk, toc.pb, etc.
# For .xapk, same approach:
unzip ../input.xapk
# Step 2: Merge into a single "fat APK" using APKEditor
java -jar APKEditor.jar m -i base.apk split_config.arm64_v8a.apk \
split_config.xxhdpi.apk split_config.en.apk -o merged.apk
# APKEditor intelligently merges resource tables from all splits.
# The resulting merged.apk is self-contained for decompilation.
# Step 3: Decompile the merged APK
apktool d merged.apk -o decompiled/ -f
# Step 4: Make your modifications (smali, resources, manifest)
# Step 5: Recompile
apktool b decompiled/ -o modified_base.apk
# Step 6: Align and sign (see Section 4)
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:
```bash
# Uninstall the app first (signature mismatch will block update)
adb uninstall com.example.app
# Install base + original splits together
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):
```bash
# Sign every APK with the same keystore
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
# Install all at once
adb install-multiple modified_base_aligned.apk splits/split_config.*.apk
```
**Option C: Create a new bundle for distribution**
```bash
# Create .apks bundle using bundletool
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
```bash
# === Creating a Custom Keystore (one-time) ===
keytool -genkey -v -keystore ~/release.keystore \
-alias myapp \
-keyalg RSA \
-keysize 2048 \
-validity 10000
# You'll be prompted for:
# - Keystore password (use something STRONG and memorable)
# - Key password (same or different)
# - Your name, organization, city, state, country code
# Store the keystore and passwords securely — you CANNOT change the key later
# === Using the Android Debug Keystore (for testing only) ===
# Location: ~/.android/debug.keystore
# Password: android
# Alias: androiddebugkey
# This keystore is identical on ALL Android SDK installations.
# DO NOT use for production — apps signed with it cannot be published or updated.
# === Full Signing Workflow ===
# Step 1: Align the APK (MUST be before signing)
zipalign -v -p 4 rebuilt.apk rebuilt_aligned.apk
# Step 2: Sign with apksigner (all three schemes for maximum compatibility)
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
# Note: v4 is NOT signed here — it's computed on-demand during installation
# Step 3: Verify the signature
apksigner verify --verbose --print-certs rebuilt_aligned.apk
# Expected output:
# Verifies
# Verified using v1 scheme (JAR signing): true
# Verified using v2 scheme (APK Signature Scheme v2): true
# Verified using v3 scheme (APK Signature Scheme v3): true
# Number of signers: 1
# Step 4: Optional — Check what schemes are present
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):
```bash
# Sign with the NEW key, providing the OLD key's lineage
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
# To extract lineage from the OLD keystore:
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:
```bash
# Install the standard Android framework (usually automatic)
apktool if framework-res.apk
# Pull framework files from a rooted device
adb pull /system/framework/ ./device_frameworks/
# Install OEM-specific frameworks
# Samsung:
apktool if device_frameworks/sec_platform_library.jar
apktool if device_frameworks/com.samsung.device.jar
apktool if device_frameworks/frameworksSamsungSDK.jar
# Xiaomi/MIUI:
apktool if device_frameworks/framework-miui-res.apk
apktool if device_frameworks/MiuiSdk.jar
apktool if device_frameworks/framework-mi.jar
# Huawei:
apktool if device_frameworks/HWExtension.jar
apktool if device_frameworks/hwsdk.jar
# OnePlus/Oplus:
apktool if device_frameworks/oplus-framework.jar
apktool if device_frameworks/oplus-res.apk
# Check what frameworks are currently installed
ls ~/.local/share/apktool/framework/
```
**Step 2: Install shared library APKs**
Some apps depend on vendor-shared library APKs:
```bash
# Find shared library APKs on device
adb shell pm list packages -f | grep shared
adb shell pm list packages -f | grep library
# Pull and install as framework
adb pull /system/app/SomeSharedLib/SomeSharedLib.apk
apktool if SomeSharedLib.apk
```
**Step 3: Force API level to resolve resource conflicts**
```bash
# If resources from newer APIs aren't found
apktool d target.apk -o out/ -f --api 35
# If dealing with legacy frameworks
apktool d target.apk -o out/ -f --api 29
```
**Step 4: Use the ignore flag for known non-critical missing resources**
```bash
# New in Apktool 2.11.x
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:
1. Decompile the original APK without modification and immediately recompile (if that fails too, it's a tool limitation)
2. Manually add the missing resource definition to `res/values/` files
3. 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:
1. **Split APK hell**: Play Services is distributed as ~100+ dynamic feature modules via split APKs
2. **Runtime signature verification**: Both the OS and other GApps verify Play Services' signature at runtime
3. **DroidGuard attestation**: Google's attestation service is deeply integrated into Play Services core
4. **Automatic updates**: The Play Store auto-updates Play Services, overwriting any patches
5. **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.
```bash
# MicroG provides:
# - Location services (via UnifiedNlp / OpenStreetMap backends)
# - Push notifications (via ntfy or custom push servers)
# - Device registration
# - Maps API (via OpenStreetMap)
# - Limited Google account support
# MicroG does NOT provide:
# - Play Integrity passing
# - Full DroidGuard attestation
# - SafetyNet compatibility
# - All Google API parity
# - Some Play Games features
# Setup MicroG:
# 1. Download GmsCore.apk from https://github.com/microg/GmsCore/releases
# Or use "MicroG RE" (Redesign for ReVanced) for better compatibility
# 2. Install on your device/emulator
# 3. Install a push notification provider (e.g., ntfy)
# 4. Configure signature spoofing (see 6.4)
```
### 6.3 Strategy 2: Patching Individual GApps (YouTube, Maps, etc.)
**ReVanced** (the successor to Vanced) provides pre-made patches:
- https://github.com/ReVanced/revanced-patches
- Patches for: YouTube, YouTube Music, Google Photos, etc.
- Handles signature bypass, ad removal, background play, etc.
**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.
```bash
# The patching approach:
# 1. Decompile the app
apktool d google_app.apk -o decompiled/
# 2. Find the signature verification code
# Search smali for these patterns:
# getPackageInfo
# GET_SIGNATURES
# PackageManager
# MessageDigest
# equals (for signature comparison)
# toByteArray (for signature byte extraction)
# 3. Patch or NOP the signature check
# Common pattern in smali:
# const-string v0, "308203e3..."
# invoke-virtual {v0}, Ljava/lang/String;->getBytes()[B
# invoke-static {...}, Ljava/security/MessageDigest;->getInstance(...)L...
# # Replace with: const/4 v_result, 0x1 (return true/valid)
```
### 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
```bash
# Install signature spoofing Magisk module:
# Search for "FakeGApps" or "sigspoof" in Magisk Modules repository
# Or use: https://github.com/microg/android_packages_apps_GmsCore/wiki/Installation
```
### 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)**:
1. Root via Magisk / KernelSU / APatch
2. Install ZygiskNext
3. Install Play Integrity Fix (or NEXT/fork)
4. Install TrickyStore + TrickyStore Addon
5. 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
```bash
# Step 1: Install Android Studio
# Download from https://developer.android.com/studio
# Step 2: Create an AVD (Virtual Device)
# Android Studio → Tools → Device Manager → Create Device
# Recommended configuration:
# - Device: Pixel 7 (or any recent phone profile)
# - System Image: API 35 (Android 15), x86_64 (for speed)
# OR ARM64 if testing ARM-specific native libs
# - RAM: 4096 MB minimum (8192 MB preferred)
# - AVD Name: "test_device_api35"
# Step 3: Root the AVD with rootAVD
pip3 install rootAVD
rootAVD -s test_device_api35 # Root by AVD name
# Step 4: Verify root
emulator -avd test_device_api35 -no-snapshot-load
adb shell su -c "id"
# Expected: uid=0(root) gid=0(root)
# Step 5: Install Magisk modules (if needed)
# Push module zip to device and flash via Magisk app
adb push module.zip /sdcard/Download/
# Then flash in Magisk Manager app on the emulator
# Step 6: Install your patched APK
adb install -r patched.apk
# For split APKs:
adb install-multiple base.apk split_config.arm64_v8a.apk split_config.en.apk
# Force downgrade if version conflict:
adb install -r -d -t patched.apk
# Clean install if signature mismatch:
adb uninstall com.example.app
adb install patched.apk
```
### 7.3 GApps Testing on Emulator
**Option A: AVD with Google APIs image**
```bash
# Create AVD with "Google APIs" system image
# Android Studio → Device Manager → Create Device
# Choose system image: "Google APIs" (not "Google Play")
# This includes Play Services but allows more flexibility
# Caveat: Pre-installed Google Play Services CANNOT be replaced
# with patched versions because the system expects Google's signature
```
**Option B: AVD without Google APIs + MicroG (recommended for GApps-free testing)**
```bash
# Create AVD with "No Google APIs" / AOSP system image
# This gives you a clean slate
# Install MicroG:
adb install GmsCore.apk
adb install ntfy.apk # Push notification provider
# Install your patched app:
adb install patched.apk
# Verify MicroG self-check:
# Open MicroG app → Self-check → All should be green (except Play Integrity)
```
### 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**:
- Uses host Linux kernel directly (container, not emulation)
- ARM translation via libndk_translation
- Requires SSE 4.2 CPU instructions
- Install via waydroid_script: https://github.com/casualsnek/waydroid_script
### 7.5 Frida Setup on Emulator
```bash
# Install Frida tools
pip install frida-tools
# Download frida-server for the emulator's architecture
# Check architecture: adb shell getprop ro.product.cpu.abi
# For x86_64 emulator: download frida-server-x86_64.xz
# For arm64 emulator: download frida-server-arm64.xz
# Push and run frida-server
adb push frida-server /data/local/tmp/
adb shell "chmod 755 /data/local/tmp/frida-server"
adb shell "/data/local/tmp/frida-server -D &"
# Verify connection
frida-ps -U # Should list running processes
# Run Frida REPL against a running app
frida -U -n com.example.app
# Spawn app with Frida injected
frida -U -f com.example.app -l hook.js --no-pause
# Run a JavaScript hook script
frida -U -l bypass_signature.js -n com.example.app
```
### 7.6 ADB Commands Reference for Testing
```bash
# === Installation ===
adb install -r app.apk # Replace existing app
adb install -d app.apk # Allow version downgrade
adb install -t app.apk # Allow test APKs
adb install-multiple base.apk s1.apk s2.apk # Split APKs
adb uninstall com.example.app # Remove app + data
# === Debugging ===
adb logcat # View logs
adb logcat -s "ActivityManager" # Filter by tag
adb logcat | grep -i "FATAL" # Filter crash logs
adb shell dumpsys package com.example.app # Package info
# === File operations ===
adb push file.txt /sdcard/ # Upload file
adb pull /sdcard/file.txt ./ # Download file
# === Root operations ===
adb shell su -c "command" # Run as root
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-specific ===
emulator -avd NAME -no-snapshot-load # Start emulator fresh
adb reboot # Reboot emulator
```
### 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
```bash
# One-time creation
keytool -genkey -v \
-keystore ~/apk-testing.keystore \
-alias testing \
-keyalg RSA \
-keysize 2048 \
-validity 10000
# You'll be prompted for:
# - Keystore password (use something STRONG and memorable)
# - Key password (same or different)
# - Your name, organization, city, state, country code
#
# BACKUP THIS FILE. If you lose it, you'll have to uninstall and reinstall every app you ever signed.
```
#### 8.1.3 The Critical Order: zipalign THEN sign
```bash
# CORRECT ORDER:
zipalign -v -p 4 rebuilt.apk rebuilt_aligned.apk # Step 1: ALIGN first
apksigner sign --ks key.keystore aligned.apk # Step 2: SIGN second
# WRONG ORDER (will break v2/v3 signatures):
apksigner sign --ks key.keystore rebuilt.apk # Signing first
zipalign -v -p 4 rebuilt.apk aligned.apk # Then aligning BREAKS the signature!
```
**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:
```bash
# Extract splits from .apks/.apkm
unzip input.apks -d splits/
# Align all APKs
for apk in splits/*.apk; do
zipalign -v -p 4 "$apk" "${apk%.apk}_aligned.apk"
done
# Sign ALL splits with THE SAME key
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
# Install all at once via ADB
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:
```bash
# Download from https://github.com/patrickfav/uber-apk-signer/releases
java -jar uber-apk-signer.jar --apks rebuilt.apk
# Handles: zipalign + sign with debug keystore + verify
# Output: rebuilt-aligned-debugSigned.apk
# With custom keystore
java -jar uber-apk-signer.jar \
--apks rebuilt.apk \
--ks ~/apk-testing.keystore \
--ksAlias testing \
--ksPass your_password
# Sign multiple APKs at once (splits)
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**:
- Docs: https://github.com/remote-android/redroid-doc
- Docker Hub: https://hub.docker.com/r/redroid/redroid
- Kernel modules: https://github.com/remote-android/redroid-modules
#### 8.2.2 Prerequisites & Kernel Setup
```bash
# 1. Install Docker
sudo apt install docker.io docker-compose
sudo usermod -aG docker $USER # Log out and back in
# 2. Install binder kernel module
sudo apt install linux-modules-extra-$(uname -r)
# 3. Load binder module
sudo modprobe binder_linux
# 4. Verify binder is available
ls /dev/binderfs 2>/dev/null && echo "OK: binderfs found" || echo "MISSING"
# 5. For Android 12+: ashmem is replaced by memfd (no extra module needed)
# For Android 11: also load ashmem
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):
```bash
docker run -d \
--privileged \
--name redroid14 \
-p 5555:5555 \
redroid/redroid:14.0.0-latest
```
**With GPU acceleration** (Intel/AMD via Mesa/virgl):
```bash
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):
```bash
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):
```yaml
# docker-compose.yml
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:
```
```bash
docker compose up -d # Start
docker compose logs -f # View boot logs (wait for "Boot completed")
docker compose down # Stop
```
#### 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
```bash
# Wait for container to boot (check logs: "Boot completed")
docker logs redroid14 2>&1 | tail -5
# Connect ADB via TCP
adb connect localhost:5555
adb devices
# Expected: localhost:5555 device
# === Install patched APK ===
# Basic install
adb -s localhost:5555 install rebuilt_aligned.apk
# Reinstall over existing
adb -s localhost:5555 install -r rebuilt_aligned.apk
# Force install (bypass version/signature conflicts)
adb -s localhost:5555 install -d -t --force-install rebuilt_aligned.apk
# Install split APKs
adb -s localhost:5555 install-multiple base_aligned.apk \
split_config.arm64_v8a_aligned.apk \
split_config.xxhdpi_aligned.apk
# Grant all runtime permissions
adb -s localhost:5555 install -r -g rebuilt_aligned.apk
# === App management ===
adb -s localhost:5555 shell pm list packages # List all
adb -s localhost:5555 shell pm list packages -3 # Third-party only
adb -s localhost:5555 uninstall com.example.app # Remove
adb -s localhost:5555 shell am force-stop com.example.app # Force stop
adb -s localhost:5555 shell am start -n com.example.app/.MainActivity # Launch
# === Shell access (root by default!) ===
adb -s localhost:5555 shell
whoami # root
id # uid=0(root) gid=0(root)
# === Screen mirroring (if you need to see the UI) ===
# Use scrcpy (install: sudo apt install scrcpy)
scrcpy -s localhost:5555
```
#### 8.2.6 GApps, Magisk & ARM Translation on Redroid
All three can be installed via [redroid-script](https://github.com/abing7k/redroid-script) without rebuilding the Docker image:
```bash
git clone https://github.com/abing7k/redroid-script.git
cd redroid-script
# Install everything in one go
sudo python3 redroid-script.py redroid14 --gapps --magisk --libndk
# Or individually:
sudo python3 redroid-script.py redroid14 --gapps # OpenGApps
sudo python3 redroid-script.py redroid14 --magisk # Magisk
sudo python3 redroid-script.py redroid14 --libndk # ARM translation (libndk_translation)
sudo python3 redroid-script.py redroid14 --houdini # ARM translation (Intel Houdini)
# Restart container after installation
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
```bash
# === One-time setup ===
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
# Wait ~30s for boot, then:
adb connect localhost:5555
# === GApps + ARM translation (one-time, optional) ===
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
# === Per-APK test cycle ===
# 1. Decompile, modify, recompile (see Section 2)
apktool d target.apk -o dec/ -f
# ... make modifications ...
apktool b dec/ -o rebuilt.apk
# 2. Sign
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
# 3. Uninstall previous version (if exists)
adb -s localhost:5555 uninstall com.example.app 2>/dev/null
# 4. Install
adb -s localhost:5555 install rebuilt_aligned.apk
# 5. Launch and check logs
adb -s localhost:5555 shell am start -n com.example.app/.MainActivity
adb -s localhost:5555 logcat -s "AndroidRuntime:E" "*:S"
# 6. If needed, mirror screen with scrcpy
scrcpy -s localhost:5555
# === Cleanup ===
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**:
- Website: https://waydro.id
- Docs: https://docs.waydro.id
- GitHub: https://github.com/waydroid/waydroid
- ArchWiki: https://wiki.archlinux.org/title/Waydroid
#### 8.3.2 Installation
```bash
# === Ubuntu 22.04 / 24.04 ===
sudo apt install curl ca-certificates
curl https://repo.waydro.id | sudo bash
sudo apt install waydroid
# === Debian ===
curl https://repo.waydro.id | sudo bash
sudo apt install waydroid
# === Arch Linux ===
sudo pacman -S waydroid
# === Fedora ===
sudo dnf install waydroid
# === Initialize (choose one) ===
sudo waydroid init # Vanilla (no Google services)
sudo waydroid init -s GAPS # With OpenGApps (Play Store etc.)
# === Start ===
waydroid session start
waydroid show-full-ui # Opens Android desktop window
```
#### 8.3.3 GApps, Magisk & ARM Translation via waydroid_script
[casualsnek/waydroid_script](https://github.com/casualsnek/waydroid_script) is the Swiss army knife for Waydroid:
```bash
git clone https://github.com/casualsnek/waydroid_script.git
cd waydroid_script
sudo pip3 install requests pyyaml # Dependencies
# Install everything:
sudo python3 waydroid_script.py -g -m -l
# -g = GApps (OpenGApps)
# -m = Magisk
# -l = libndk_translation (ARM translation)
# Individual options:
sudo python3 waydroid_script.py -g # GApps only
sudo python3 waydroid_script.py -g --variant pico # GApps, minimal variant
sudo python3 waydroid_script.py -g --microg # MicroG instead of OpenGApps
sudo python3 waydroid_script.py -m # Magisk only
sudo python3 waydroid_script.py -l # libndk_translation
sudo python3 waydroid_script.py -L # libhoudini (alternative ARM translation)
# Restart Waydroid after installation
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:
```bash
# Check
cat /proc/cpuinfo | grep sse4_2
# If empty: your CPU doesn't support it — ARM translation won't work
```
#### 8.3.4 ADB Connection & APK Installation
```bash
# Waydroid exposes ADB via local socket (auto-configured)
adb devices
# Expected: waydroid0 device
# === Install patched APK ===
# Using waydroid CLI (recommended)
waydroid app install /path/to/rebuilt_aligned.apk
# Using ADB directly
adb install /path/to/rebuilt_aligned.apk
adb install -r /path/to/rebuilt_aligned.apk # Reinstall
adb install -d -t --force-install /path/to/app.apk # Force
# Install split APKs via ADB
adb install-multiple base.apk split_config.arm64_v8a.apk
# === App management ===
waydroid app list # List installed packages
waydroid app launch com.example.app # Launch app
waydroid app remove com.example.app # Uninstall
waydroid app list --show-package-name # Show package names
# === Shell access ===
adb shell # Regular shell
sudo waydroid shell # Root shell (if configured)
# === Screen recording / screenshots ===
waydroid ctrl +p # Screenshot (shortcut, if mapped)
```
#### 8.3.5 Complete Waydroid APK Testing Workflow
```bash
# === One-time setup ===
curl https://repo.waydro.id | sudo bash
sudo apt install waydroid
sudo waydroid init # or: sudo waydroid init -s GAPS
# Install ARM translation + Magisk
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
# === Per-APK test cycle ===
# 1. Decompile, modify, recompile
apktool d target.apk -o dec/ -f
# ... make modifications ...
apktool b dec/ -o rebuilt.apk
# 2. Sign
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
# 3. Uninstall previous version
waydroid app remove com.example.app 2>/dev/null
# 4. Install
waydroid app install rebuilt_aligned.apk
# 5. Launch
waydroid app launch com.example.app
# 6. Check logs
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:
```bash
# Workaround 1: Force software rendering
WAYDROID_DISABLE_GPU=1 waydroid session start
# Workaround 2: Switch to integrated GPU (dual-GPU laptops)
# Use prime-select or DRI_PRIME=1
# Workaround 3: NVIDIA open kernel module 575.51.02+
# Recent reports (mid-2025) show Waydroid "works" with open driver
# But GPU acceleration may still be partial
```
#### 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](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](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](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](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](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](references/quick-reference.md)
Includes:
- Command summary (decompile, recompile, align, sign, install)
- Smali quick reference
- Key file paths
- Common patterns cheat sheet.
## References
- Redroid documentation: https://github.com/remote-android/redroid-doc
- Redroid Docker Hub: https://hub.docker.com/r/redroid/redroid
- Waydroid website: https://waydro.id
- Waydroid documentation: https://docs.waydro.id
- Waydroid GitHub: https://github.com/waydroid/waydroid
- casualsnek/waydroid_script: https://github.com/casualsnek/waydroid_script