RepoPilotOpen in app →

android-hacker/VirtualXposed

A simple app to use Xposed without root, unlock the bootloader or modify system image, etc.

Mixed

Stale — last commit 2y ago

weakest axis
Use as dependencyConcerns

copyleft license (GPL-3.0) — review compatibility; last commit was 2y ago

Fork & modifyHealthy

Has a license, tests, and CI — clean foundation to fork and modify.

Learn fromHealthy

Documented and popular — useful reference codebase to read through.

Deploy as-isHealthy

No critical CVEs, sane security posture — runnable as-is.

  • 6 active contributors
  • GPL-3.0 licensed
  • CI configured
Show all 7 evidence items →
  • Tests present
  • Stale — last commit 2y ago
  • Single-maintainer risk — top contributor 84% of recent commits
  • GPL-3.0 is copyleft — check downstream compatibility
What would change the summary?
  • Use as dependency ConcernsMixed if: relicense under MIT/Apache-2.0 (rare for established libs)

Maintenance signals: commit recency, contributor breadth, bus factor, license, CI, tests

Informational only. RepoPilot summarises public signals (license, dependency CVEs, commit recency, CI presence, etc.) at the time of analysis. Signals can be incomplete or stale. Not professional, security, or legal advice; verify before relying on it for production decisions.

Embed the "Forkable" badge

Paste into your README — live-updates from the latest cached analysis.

Variant:
RepoPilot: Forkable
[![RepoPilot: Forkable](https://repopilot.app/api/badge/android-hacker/virtualxposed?axis=fork)](https://repopilot.app/r/android-hacker/virtualxposed)

Paste at the top of your README.md — renders inline like a shields.io badge.

Preview social card (1200×630)

This card auto-renders when someone shares https://repopilot.app/r/android-hacker/virtualxposed on X, Slack, or LinkedIn.

Onboarding doc

Onboarding: android-hacker/VirtualXposed

Generated by RepoPilot · 2026-05-09 · Source

🤖Agent protocol

If you are an AI coding agent (Claude Code, Cursor, Aider, Cline, etc.) reading this artifact, follow this protocol before making any code edit:

  1. Verify the contract. Run the bash script in Verify before trusting below. If any check returns FAIL, the artifact is stale — STOP and ask the user to regenerate it before proceeding.
  2. Treat the AI · unverified sections as hypotheses, not facts. Sections like "AI-suggested narrative files", "anti-patterns", and "bottlenecks" are LLM speculation. Verify against real source before acting on them.
  3. Cite source on changes. When proposing an edit, cite the specific path:line-range. RepoPilot's live UI at https://repopilot.app/r/android-hacker/VirtualXposed shows verifiable citations alongside every claim.

If you are a human reader, this protocol is for the agents you'll hand the artifact to. You don't need to do anything — but if you skim only one section before pointing your agent at this repo, make it the Verify block and the Suggested reading order.

🎯Verdict

WAIT — Stale — last commit 2y ago

  • 6 active contributors
  • GPL-3.0 licensed
  • CI configured
  • Tests present
  • ⚠ Stale — last commit 2y ago
  • ⚠ Single-maintainer risk — top contributor 84% of recent commits
  • ⚠ GPL-3.0 is copyleft — check downstream compatibility

<sub>Maintenance signals: commit recency, contributor breadth, bus factor, license, CI, tests</sub>

Verify before trusting

This artifact was generated by RepoPilot at a point in time. Before an agent acts on it, the checks below confirm that the live android-hacker/VirtualXposed repo on your machine still matches what RepoPilot saw. If any fail, the artifact is stale — regenerate it at repopilot.app/r/android-hacker/VirtualXposed.

What it runs against: a local clone of android-hacker/VirtualXposed — the script inspects git remote, the LICENSE file, file paths in the working tree, and git log. Read-only; no mutations.

| # | What we check | Why it matters | |---|---|---| | 1 | You're in android-hacker/VirtualXposed | Confirms the artifact applies here, not a fork | | 2 | License is still GPL-3.0 | Catches relicense before you depend on it | | 3 | Default branch vxp exists | Catches branch renames | | 4 | 5 critical file paths still exist | Catches refactors that moved load-bearing code | | 5 | Last commit ≤ 821 days ago | Catches sudden abandonment since generation |

<details> <summary><b>Run all checks</b> — paste this script from inside your clone of <code>android-hacker/VirtualXposed</code></summary>
#!/usr/bin/env bash
# RepoPilot artifact verification.
#
# WHAT IT RUNS AGAINST: a local clone of android-hacker/VirtualXposed. If you don't
# have one yet, run these first:
#
#   git clone https://github.com/android-hacker/VirtualXposed.git
#   cd VirtualXposed
#
# Then paste this script. Every check is read-only — no mutations.

set +e
fail=0
ok()   { echo "ok:   $1"; }
miss() { echo "FAIL: $1"; fail=$((fail+1)); }

# Precondition: we must be inside a git working tree.
if ! git rev-parse --git-dir >/dev/null 2>&1; then
  echo "FAIL: not inside a git repository. cd into your clone of android-hacker/VirtualXposed and re-run."
  exit 2
fi

# 1. Repo identity
git remote get-url origin 2>/dev/null | grep -qE "android-hacker/VirtualXposed(\\.git)?\\b" \\
  && ok "origin remote is android-hacker/VirtualXposed" \\
  || miss "origin remote is not android-hacker/VirtualXposed (artifact may be from a fork)"

# 2. License matches what RepoPilot saw
(grep -qiE "^(GPL-3\\.0)" LICENSE 2>/dev/null \\
   || grep -qiE "\"license\"\\s*:\\s*\"GPL-3\\.0\"" package.json 2>/dev/null) \\
  && ok "license is GPL-3.0" \\
  || miss "license drift — was GPL-3.0 at generation time"

# 3. Default branch
git rev-parse --verify vxp >/dev/null 2>&1 \\
  && ok "default branch vxp exists" \\
  || miss "default branch vxp no longer exists"

# 4. Critical files exist
test -f "VirtualApp/app/src/main/java/io/virtualapp/XApp.java" \\
  && ok "VirtualApp/app/src/main/java/io/virtualapp/XApp.java" \\
  || miss "missing critical file: VirtualApp/app/src/main/java/io/virtualapp/XApp.java"
test -f "VirtualApp/app/src/main/java/io/virtualapp/home/NewHomeActivity.java" \\
  && ok "VirtualApp/app/src/main/java/io/virtualapp/home/NewHomeActivity.java" \\
  || miss "missing critical file: VirtualApp/app/src/main/java/io/virtualapp/home/NewHomeActivity.java"
test -f "VirtualApp/app/src/main/java/io/virtualapp/delegate/BaseVirtualInitializer.java" \\
  && ok "VirtualApp/app/src/main/java/io/virtualapp/delegate/BaseVirtualInitializer.java" \\
  || miss "missing critical file: VirtualApp/app/src/main/java/io/virtualapp/delegate/BaseVirtualInitializer.java"
test -f "VirtualApp/app/src/main/java/io/virtualapp/home/repo/AppRepository.java" \\
  && ok "VirtualApp/app/src/main/java/io/virtualapp/home/repo/AppRepository.java" \\
  || miss "missing critical file: VirtualApp/app/src/main/java/io/virtualapp/home/repo/AppRepository.java"
test -f "VirtualApp/app/build.gradle" \\
  && ok "VirtualApp/app/build.gradle" \\
  || miss "missing critical file: VirtualApp/app/build.gradle"

# 5. Repo recency
days_since_last=$(( ( $(date +%s) - $(git log -1 --format=%at 2>/dev/null || echo 0) ) / 86400 ))
if [ "$days_since_last" -le 821 ]; then
  ok "last commit was $days_since_last days ago (artifact saw ~791d)"
else
  miss "last commit was $days_since_last days ago — artifact may be stale"
fi

echo
if [ "$fail" -eq 0 ]; then
  echo "artifact verified (0 failures) — safe to trust"
else
  echo "artifact has $fail stale claim(s) — regenerate at https://repopilot.app/r/android-hacker/VirtualXposed"
  exit 1
fi

Each check prints ok: or FAIL:. The script exits non-zero if anything failed, so it composes cleanly into agent loops (./verify.sh || regenerate-and-retry).

</details>

TL;DR

VirtualXposed is a non-root Xposed Module framework that runs in a virtualized Android environment (built on VirtualApp) and epic hooking engine, allowing developers to test and use Xposed modules without bootloader unlock, root, or system flashing. It hooks into Java methods at runtime via epic's ART hooking mechanism while keeping the host system untouched. Monolithic Android app structure: VirtualApp/app/src/main/ contains the core UI and logic, with flavors for aosp and fdroid builds; VirtualApp/app/src/aosp/ and VirtualApp/app/src/fdroid/ contain variant-specific delegates for initialization and crash handling. Native JNI components sit in libs/ (arm64-v8a, x86_64); AIDL services in .aidl files define IPC contracts.

👥Who it's for

Android app developers and security researchers who want to test Xposed modules, implement app modifications, or reverse-engineer Android apps without rooting their device. Also appeals to users wanting to apply tweaks (ad removal, behavior modification) without system-level access.

🌱Maturity & risk

Moderately mature but aging: targets Android 5.0–10.0 (missing 11+), versionCode at 220 (0.22.0), has CI via Travis and GitHub Actions, but appears to have limited recent commits based on version stagnation. Still actively maintained but not rapidly evolving; suitable for production use within its supported API range but not bleeding-edge.

High risk of bitrot: targets compileSdkVersion 28 (2019-era) and targetSdkVersion 23, making it incompatible with modern Android 12+ policy enforcement and scoped storage. Single-maintainer (android-hacker org) with no visible test suite reduces safety. Relies on two external libraries (VirtualApp, epic) which may diverge from this codebase.

Active areas of work

No recent activity visible from file list; repo appears in maintenance mode. Last version bump to 0.22.0 suggests no active feature development. GitHub Actions workflow (android.yml) is configured for CI but commit frequency unknown.

🚀Get running

git clone https://github.com/android-hacker/VirtualXposed.git
cd VirtualXposed/VirtualApp
# Configure local.properties with keystore if building aospFlavor
echo 'keystore.path=/path/to/keystore' >> local.properties
# Build with Gradle
./gradlew assembleAosp  # or assembleFdroid

Daily commands: Build via Gradle: ./gradlew assembleAosp or ./gradlew assembleFdroid. Install APK: adb install build/outputs/apk/.../app-aosp-release.apk. Launch via Android Studio emulator or physical device running Android 5.0–10.0.

🗺️Map of the codebase

  • VirtualApp/app/src/main/java/io/virtualapp/XApp.java — Main Application class that initializes VirtualApp framework and Xposed module injection system — entry point for all virtual environment setup
  • VirtualApp/app/src/main/java/io/virtualapp/home/NewHomeActivity.java — Primary UI entry point after launch where users manage virtual apps and Xposed modules — core user interaction surface
  • VirtualApp/app/src/main/java/io/virtualapp/delegate/BaseVirtualInitializer.java — Abstract initializer that bridges VirtualApp lifecycle with Xposed hook system — critical integration point between frameworks
  • VirtualApp/app/src/main/java/io/virtualapp/home/repo/AppRepository.java — Data access layer managing virtual app installation, uninstallation, and metadata persistence — central data flow controller
  • VirtualApp/app/build.gradle — Build configuration defining dependencies (VirtualApp, epic), signing config, and target SDK — controls entire build pipeline
  • VirtualApp/app/src/main/AndroidManifest.xml — Manifest defining all activities, services, and required permissions for virtualization framework operation
  • VirtualApp/app/src/main/java/io/virtualapp/delegate/MyComponentDelegate.java — Component lifecycle delegate intercepting activity/service creation to inject Xposed hooks into virtual apps

🛠️How to make changes

Add a new Xposed Hook for a System API

  1. Create a custom hook implementation extending XposedBridge callback in a new file under VirtualApp/app/src/main/java/io/virtualapp/delegate/ (VirtualApp/app/src/main/java/io/virtualapp/delegate/MyComponentDelegate.java)
  2. Register the hook in BaseVirtualInitializer.onVirtualAppStarted() to be installed when modules load (VirtualApp/app/src/main/java/io/virtualapp/delegate/BaseVirtualInitializer.java)
  3. Update MyComponentDelegate to activate the hook during activity/service creation lifecycle (VirtualApp/app/src/main/java/io/virtualapp/delegate/MyComponentDelegate.java)

Add a new Virtual App Management Feature (UI + Logic)

  1. Define a new view contract in VirtualApp/app/src/main/java/io/virtualapp/home/ extending BaseView interface (VirtualApp/app/src/main/java/io/virtualapp/home/ListAppContract.java)
  2. Create a presenter implementation extending BasePresenter to handle business logic (VirtualApp/app/src/main/java/io/virtualapp/home/ListAppPresenterImpl.java)
  3. Create a new Activity/Fragment in VirtualApp/app/src/main/java/io/virtualapp/home/ implementing the view (VirtualApp/app/src/main/java/io/virtualapp/home/NewHomeActivity.java)
  4. Add AppRepository query method in VirtualApp/app/src/main/java/io/virtualapp/home/repo/AppRepository.java (VirtualApp/app/src/main/java/io/virtualapp/home/repo/AppRepository.java)
  5. Register activity in AndroidManifest.xml with required intent-filters (VirtualApp/app/src/main/AndroidManifest.xml)

Customize Virtual Environment Initialization

  1. Extend BaseVirtualInitializer in a flavor-specific package (aosp/ or fdroid/) to customize startup behavior (VirtualApp/app/src/aosp/java/io/virtualapp/delegate/MyVirtualInitializer.java)
  2. Override onVirtualAppStarted() to inject custom hooks or initialize modules before app launch (VirtualApp/app/src/main/java/io/virtualapp/delegate/BaseVirtualInitializer.java)
  3. Override onVirtualAppStopped() to cleanup resources and persisted state (VirtualApp/app/src/main/java/io/virtualapp/delegate/BaseVirtualInitializer.java)
  4. Rebuild with custom flavor in build.gradle to test initialization changes (VirtualApp/app/build.gradle)

🔧Why these technologies

  • VirtualApp Framework — Enables sandboxed app environment without root access; isolates virtual apps from system and each other
  • epic (Xposed Implementation) — Provides runtime method hooking mechanism to intercept and modify system API behavior for virtual apps
  • Android MVP Pattern — Separates UI logic from business logic; improves testability and maintainability across activities and fragments
  • Gradle Build System — Manages dependencies (

🪤Traps & gotchas

Keystore configuration: aosp flavor requires local.properties with keystore.path, keystore.alias, keystore.pwd, keystore.alias_pwd or builds fail silently. NDK ABI filters: only arm64-v8a and x86_64 are included; x86 devices cannot run this build. Android version ceiling: targetSdkVersion 23 triggers scoped storage and permission model changes on Android 10+; dynamic permissions likely incomplete. Epic/VirtualApp coupling: breaking changes in either dependency (especially epic's hooking API) will silently break method interception with no compile errors.

🏗️Architecture

💡Concepts to learn

  • ART Method Hooking — epic's core mechanism for intercepting Java method calls at the ART runtime level; understanding this explains why VirtualXposed can modify app behavior without system access
  • Process Virtualization — VirtualApp creates isolated process namespaces where apps run in a sandbox; critical for isolating Xposed modules' side effects from the host system
  • AIDL (Android Interface Definition Language) — Enables inter-process communication between the virtual environment and host; used to hook system services and relay method calls
  • JNI (Java Native Interface) — Bridge between Java code and C++ native libraries (in libs/) for low-level hooking and process manipulation; compiled for arm64-v8a and x86_64
  • Android Product Flavors — VirtualXposed uses aosp and fdroid flavors to build variant APKs with different initialization delegates and signing strategies; essential for maintaining open-source and play-store variants
  • MVP (Model-View-Presenter) — BasePresenter/BaseView/BaseActivity structure separates UI logic from business logic; guides where to add new features in the codebase
  • Xposed Module API — VirtualXposed mimics the Xposed framework's hook registration and lifecycle; understanding how modules declare hooks (XC_MethodHook, XC_MethodReplacement) is essential for testing modules
  • asLody/VirtualApp — Core dependency providing app virtualization engine; VirtualXposed wraps this to create isolated process environment
  • tiann/epic — ART hooking engine used for method interception; fundamental to how Xposed modules actually hook and modify behavior
  • rovo89/Xposed — Original Xposed Framework; VirtualXposed emulates its module API and behavior without requiring system-level access
  • Elder-Scroller/CorePatch — Alternative non-root hooking approach; similar goal of runtime method modification without system flashing
  • Xposed-Developers/XposedInstaller — Official Xposed module manager; bundled as XposedInstaller_3.1.5.apk_ in assets and installed into virtual environment

🪄PR ideas

To work on one of these in Claude Code or Cursor, paste: Implement the "<title>" PR idea from CLAUDE.md, working through the checklist as the task list.

Add instrumentation tests for VirtualApp module initialization and Xposed hook injection

The repo contains critical runtime initialization code (MyVirtualInitializer.java, BaseVirtualInitializer.java) and hook delegation (MyComponentDelegate.java, MyPhoneInfoDelegate.java) but lacks instrumentation tests. This is high-value because VirtualXposed's core functionality depends on proper VM initialization and hook execution. Tests would verify that Xposed modules load correctly in the virtual environment and that hook callbacks fire as expected.

  • [ ] Create androidTest directory structure under VirtualApp/app/src/androidTest/java/io/virtualapp/
  • [ ] Add instrumentation test class for BaseVirtualInitializer.java to verify delegate callbacks are invoked
  • [ ] Add instrumentation test class for MyComponentDelegate.java and MyPhoneInfoDelegate.java to verify hook interception works
  • [ ] Update build.gradle with androidTestImplementation dependencies (junit, espresso, mockito-android)
  • [ ] Reference these tests in .github/workflows/android.yml CI pipeline

Implement GitHub Actions workflow for automated APK signing and release publishing

The repo has .travis.yml (outdated service) and .github/workflows/android.yml exists but the build.gradle signing config requires local.properties with keystore credentials. This PR would add a secure GitHub Actions workflow that signs APKs using GitHub Secrets and automatically publishes releases to the releases page, eliminating manual signing steps and enabling continuous delivery.

  • [ ] Create .github/workflows/release.yml to trigger on git tags (v*)
  • [ ] Add steps to decode base64-encoded keystore from GitHub Secret into temporary keystore file
  • [ ] Inject keystore credentials via environment variables into build.gradle signing config
  • [ ] Build APK for both 'aosp' and 'fdroid' flavors
  • [ ] Use softprops/action-gh-release to publish signed APKs and changelog to releases page
  • [ ] Document required GitHub Secrets setup in CONTRIBUTING.md or README.md

Add unit tests for package installation/hooking logic in ListAppPresenterImpl.java and app delegation classes

ListAppPresenterImpl.java handles critical app installation logic and state management but has no unit test coverage. Similarly, delegate classes (MyAppRequestListener.java, MyTaskDescDelegate.java) contain important app lifecycle and permission handling logic. Unit tests would improve maintainability and catch regressions when modifying app installation or hook behavior.

  • [ ] Create test directory under VirtualApp/app/src/test/java/io/virtualapp/home/
  • [ ] Add ListAppPresenterImplTest.java with tests for installApp(), uninstallApp(), and state callbacks using Mockito
  • [ ] Create VirtualApp/app/src/test/java/io/virtualapp/delegate/ directory
  • [ ] Add MyAppRequestListenerTest.java and MyTaskDescDelegateTest.java mocking VirtualApp framework
  • [ ] Update build.gradle with testImplementation for junit, mockito, robolectric
  • [ ] Reference new test suites in .github/workflows/android.yml

🌿Good first issues

  • Add unit tests for XApp initialization: VirtualApp/app/src/main/java/io/virtualapp/ lacks JUnit tests; create tests/io/virtualapp/XAppTest.java covering initialization sequence and module loading paths.
  • Document AIDL contract: Create VirtualApp/app/src/main/aidl/*.aidl documentation describing IPC methods and their purposes; currently undocumented, making integration harder.
  • Update targetSdkVersion to 28+: Current targetSdkVersion is 23; bump to 28 or 29 and handle scoped storage, runtime permissions, and gesture navigation to support Android 11+ devices.

Top contributors

Click to expand

📝Recent commits

Click to expand
  • 122beb3 — 0.22.0 (tiann)
  • 1e94179 — minor fixes (tiann)
  • 6978d7b — Android 12: make component lazy load (tiann)
  • 5c9a95f — Android 12: Fix junit (tiann)
  • a2a5244 — Android 12: Fix package info (tiann)
  • f224868 — Android 12: Fix listenWithEventList (tiann)
  • c38673a — Android 12: Fix registerReciverWithFeature (tiann)
  • 056dc68 — Android 12: Fix ClientTransactionHandler (tiann)
  • 6bb556d — Android 12: Fix provider hook (tiann)
  • 065eea3 — Android 12: Fix splash screen (tiann)

🔒Security observations

  • High · Hardcoded Keystore Credentials in Build Configuration — VirtualApp/app/build.gradle (signingConfigs section). The build.gradle file reads keystore credentials (keystore.path, keystore.alias, keystore.pwd, keystore.alias_pwd) from local.properties file. These sensitive credentials are loaded and used in signing configuration. If local.properties is accidentally committed to version control or the credentials are exposed, attackers can sign malicious APKs with the same signing key. Fix: 1. Ensure local.properties is in .gitignore (verify it exists). 2. Use Android Studio's built-in keystore management or environment variables. 3. Implement keystore password masking and never commit credentials. 4. Consider using CI/CD secret management systems (GitHub Secrets, GitLab CI Variables) instead of local properties files.
  • High · Incomplete Gradle Configuration - Syntax Error — VirtualApp/app/build.gradle (lintOptions section, line with 'abortOnError fal'). The lintOptions section in build.gradle has 'abortOnError fal' which appears to be a truncated or incomplete configuration (likely meant to be 'false'). This syntax error could cause build failures or unexpected behavior, and the incomplete line suggests the file may have been corrupted or improperly edited. Fix: Complete the configuration by changing 'abortOnError fal' to 'abortOnError false'. Implement code review processes to catch incomplete configurations before commit.
  • Medium · Outdated Android Build Tools and SDK — VirtualApp/app/build.gradle (compileSdkVersion and buildToolsVersion). The project uses compileSdkVersion 28 and buildToolsVersion 28.0.3, which are significantly outdated (released in 2018). Android SDK 28 has reached end-of-life and no longer receives security updates. This exposes the application to known vulnerabilities in the Android framework and build toolchain. Fix: Update compileSdkVersion to at least 33-34 (Android 13-14) and buildToolsVersion accordingly. Review and update all dependencies to compatible versions. Test thoroughly on multiple Android versions after upgrading.
  • Medium · Low Target SDK Version — VirtualApp/app/build.gradle (targetSdkVersion). The targetSdkVersion is set to 23 (Android 6.0, released 2015). Modern Android requires targetSdkVersion to be at least 31 for Play Store distribution. Low targetSdkVersion means the app doesn't follow current Android security best practices and may have compatibility issues with newer Android versions. Fix: Increase targetSdkVersion to at least 31-33. Address any permission or behavior changes required for higher API levels. Test thoroughly on Android 12+ devices.
  • Medium · Minification Disabled in Release Builds — VirtualApp/app/build.gradle (buildTypes.release section). The release build type has 'minifyEnabled false', meaning ProGuard/R8 code obfuscation is disabled. This makes reverse engineering significantly easier and exposes the application logic, making it vulnerable to tampering and intellectual property theft. Fix: Enable minification by setting 'minifyEnabled true' and provide proper ProGuard rules via proguard-rules.pro file. Configure appropriate rule exceptions for essential classes to prevent functionality breakage.
  • Medium · Debug Flag Enabled Consideration — VirtualApp/app/build.gradle (buildTypes.release section). While 'debuggable false' is correctly set for release builds, the overall architecture (VirtualApp-based app that modifies system behavior) presents significant security risks. The app requires ability to hook system calls and intercept component behavior. Fix: Maintain debuggable=false in releases. Implement additional runtime integrity checks, certificate pinning for network communications, and obfuscation of critical code paths.
  • Medium · No Visible Dependency Management or Security Scanning — VirtualApp/app/build.gradle (missing dependencies block in provided snippet). The provided build.gradle snippet doesn't show any dependency declaration section. Critical dependencies like VirtualApp, epic, Glide, and others are not visible with version constraints. Without explicit version pinning and security scanning, the project may inadvertently include vulnerable transitive dependencies. Fix: 1. Implement explicit dependency version pinning in

LLM-derived; treat as a starting point, not a security audit.


Generated by RepoPilot. Verdict based on maintenance signals — see the live page for receipts. Re-run on a new commit to refresh.

Mixed signals · android-hacker/VirtualXposed — RepoPilot