157 lines
8.6 KiB
Markdown
157 lines
8.6 KiB
Markdown
# CamSwapper — Project Status
|
|
|
|
## Build Status
|
|
|
|
- **Both flavors compile**: `modernDebug` and `legacyDebug` assemble successfully with 0 errors, 0 warnings (minor `extractNativeLibs` manifest warning from AGP).
|
|
- **On-device test**: Installed on Pixel 9a (Android 16). FGS crash resolved with `specialUse` type. App now has Application class, INTERNET + POST_NOTIFICATIONS permissions, and custom notification icon.
|
|
- **Min SDK**: 29 | **Target SDK**: 36 | **Compile SDK**: 36
|
|
- **Kotlin**: JVM 11, toolchain 17
|
|
- **Gradle**: 8.13
|
|
|
|
## Recent Changes
|
|
|
|
1. **Removed Xposed framework dependency** — all Xposed APIs (IXposedHookLoadPackage, ReflectionTools, process-based hooks) deleted.
|
|
2. **Cleaned up Xposed remnants** — removed `src/modern/resources/META-INF/xposed/`, `src/legacy/AndroidManifest.xml` (xposed metadata), and `src/legacy/assets/` (xposed_init files).
|
|
3. **Moved `ConfigRepository.kt`** from `src/modern/java/` to `src/main/java/` so both product flavors can compile (MediaType/MediaSource enums needed by main source set files).
|
|
4. **Rewrote `VirtualCameraService.kt`** (485 corrupted lines → 327 clean lines):
|
|
- Uses reflection for `@SystemApi` VDM types (`VirtualDeviceManager`, `VirtualDeviceParams`, `VirtualCameraConfig`, `VirtualCameraCallback`, `DeviceFilter.Builder`)
|
|
- Uses direct API for public companion types (`CompanionDeviceManager`, `AssociationRequest`, `AssociationInfo`)
|
|
- Single clean `VirtualCameraCallback` proxy wired to `VideoPusher` pipeline
|
|
- Proper CDM association flow with fallback to dummy ID
|
|
- Removed 4x duplicated callback blocks, broken braces, dangling code
|
|
5. **Fixed `foregroundServiceType` for SDK 36** — On-device test on Pixel 9a (Android 16) revealed `mediaProjection` FGS type requires `FOREGROUND_SERVICE_MEDIA_PROJECTION` + `CAPTURE_VIDEO_OUTPUT` permissions on target SDK 36. Changed to `specialUse` with `PROPERTY_SPECIAL_USE_FGS_SUBTYPE` declaration. APK now installs without crash on boot.
|
|
6. **Added `CamSwapperApp` Application class** — `GlobalState.appContext` was `lateinit` and never initialized, causing potential crash when `VideoPusher.start()` or `SourceManager` accessed it. Now properly initialized in `Application.onCreate()`.
|
|
7. **Added missing permissions** — `INTERNET` (needed by ExoPlayer/Media3), `POST_NOTIFICATIONS` (for Android 13+ notification permission compliance).
|
|
8. **Replaced notification icon** — `android.R.drawable.ic_menu_camera` (system resource, may be unavailable) replaced with custom `R.drawable.ic_notification`.
|
|
|
|
## Architecture Overview
|
|
|
|
```
|
|
app/src/main/java/com/nothing/camera2magic/
|
|
├── MainActivity.kt # Entry point, permission flow, service bind
|
|
├── VirtualCameraService.kt # Foreground service → VirtualDeviceManager via reflection
|
|
├── GlobalState.kt # Global app context holder
|
|
├── viewmodel/
|
|
│ ├── ConfigRepository.kt # SharedPreferences-backed config (MediaType, MediaSource enums)
|
|
│ ├── SpotlightViewModel.kt # Media selection & preview state (MVI + StateFlow)
|
|
│ ├── SettingsViewModel.kt # App settings + HAL mode toggle
|
|
│ ├── ViewModelFactory.kt # ViewModel provider factory
|
|
│ └── CompositionLocals.kt # Compose DI locals
|
|
├── view/
|
|
│ ├── SpotlightView.kt # Media source selector, preview grid, module switch
|
|
│ └── SettingsView.kt # Toggle settings + HAL mode config
|
|
├── hook/
|
|
│ ├── SourceManager.kt # Media content resolver & fingerprint checking (224 lines)
|
|
│ ├── VideoPusher.kt # ExoPlayer → Surface → VirtualCameraRenderer pipeline
|
|
│ ├── VirtualCameraRenderer.kt # OpenGL ES renderer, SurfaceTexture → EGL → ImageWriter (387 lines)
|
|
│ ├── NativeBridge.kt # JNI bridge for native C++ camera HAL hooks
|
|
│ ├── HalConfigManager.kt # Root-based HAL config writer (/data/local/camera_magic/)
|
|
│ ├── BlackHoleMapper.kt # Empty/dummy camera HAL mapper
|
|
│ └── CameraState.kt # Camera state tracking
|
|
├── ui/theme/
|
|
│ ├── Color.kt, Theme.kt, Type.kt # Material3 theme
|
|
└── utils/
|
|
└── Dog.kt # Logging utility
|
|
```
|
|
|
|
## Product Flavors
|
|
|
|
| Flavor | Purpose |
|
|
|---------|---------|
|
|
| `legacy` | Devices without VirtualDeviceManager support (Android 10-13) — currently identical to modern |
|
|
| `modern` | Devices with Android 14+ VirtualDeviceManager API — has additional hardware buffer path available |
|
|
|
|
## Pipeline: Media → Virtual Camera
|
|
|
|
```
|
|
Local Media (ContentResolver) or Network (RTSP)
|
|
│
|
|
▼
|
|
VideoPusher (ExoPlayer) / SourceManager
|
|
│
|
|
▼
|
|
VirtualCameraRenderer (OpenGL ES)
|
|
│ SurfaceTexture → EGL → ImageWriter
|
|
▼
|
|
VirtualCameraService (reflection)
|
|
│ VirtualDeviceManager → VirtualCamera
|
|
▼
|
|
Other apps see a virtual camera device
|
|
```
|
|
|
|
## Component Details
|
|
|
|
### VirtualCameraService (reflection-based)
|
|
- **Status**: Compiles ✅, refactored ✅ (327 lines, clean architecture)
|
|
- **API level gate**: Build.VERSION_CODES.UPSIDE_DOWN_CAKE (34)
|
|
- **Approach**: Reflection for `@SystemApi` types (`VirtualDeviceManager`, `VirtualDeviceParams`, `VirtualCameraConfig`, `VirtualCameraCallback`, `DeviceFilter.Builder`). Direct API for public companion types (`CompanionDeviceManager`, `AssociationRequest`, `AssociationInfo`, `DeviceFilter`)
|
|
- **Callback**: `VirtualCameraCallback` proxy wired — `onStreamConfigured()` feeds the `Surface` to `VideoPusher.start()`
|
|
- **CDM**: Full `CompanionDeviceManager.associate()` flow with `DeviceFilter.FEATURE_VIRTUAL_CAMERA` filter, plus fallback to dummy ID 1 when CDM unavailable or association fails
|
|
- **VideoPusher integration**: Uses `VideoPusher` singleton object directly (`VideoPusher.start()`, `VideoPusher.stop()`)
|
|
- **Previously**: 485 corrupted lines with 4x duplicated callback blocks, broken braces, dangling code, no callback wiring, `VideoPusher.getInstance()` dependency that didn't exist
|
|
|
|
### ConfigRepository
|
|
- **Status**: Clean ✅
|
|
- Backs all settings via SharedPreferences
|
|
- Enums: `MediaSource` (LOCAL/NETWORK), `MediaType` (VIDEO/IMAGE)
|
|
- HAL mode settings also stored here
|
|
|
|
### SourceManager
|
|
- **Status**: ✅ Compiles, legacy footprint from Xposed era
|
|
- Global singleton, manages media source selection & fingerprinting
|
|
- Directly accesses ContentResolver for local media
|
|
- Still uses `moduleEnabled` concept from Xposed module days
|
|
|
|
### VideoPusher + VirtualCameraRenderer
|
|
- **Status**: ✅ Compiles (OpenGL ES + ExoPlayer pipeline)
|
|
- VideoPusher: Singleton object, takes a video ID via `start(surfaces, width, height, videoId)`, creates ExoPlayer, feeds to renderer surfaces
|
|
- VirtualCameraRenderer: 387-line OpenGL ES 2.0 renderer, handles EGL setup, SurfaceTexture → ImageWriter pipeline
|
|
- VideoPusher is wired via `VirtualCameraCallback.onStreamConfigured()` in VirtualCameraService
|
|
|
|
### HalConfigManager
|
|
- **Status**: ✅ Compiles, requires root
|
|
- Writes config to `/data/local/camera_magic/config.txt` via `su -c`
|
|
- Called from SettingsViewModel HAL mode toggle
|
|
|
|
## Remaining Work / Known Issues
|
|
|
|
### Critical
|
|
- (none — all previously identified critical issues resolved)
|
|
|
|
### Medium
|
|
- [ ] **SourceManager duplicates ConfigRepository** — SourceManager has its own SharedPreferences keys and module state, duplicating ConfigRepository. Should be consolidated.
|
|
- [ ] **Native bridge stub** — `NativeBridge.kt` exists but no native `.so` is loaded (CMake/NDK build disabled). The project uses prebuilt `libcamera3.so` from jniLibs.
|
|
- [ ] **legacy flavor** — Both flavors currently have identical source sets. The `legacy` flavor should ideally strip out VirtualDeviceManager-dependent features.
|
|
|
|
### Low / Cleanup
|
|
- [ ] **Test files** — `ExampleInstrumentedTest.kt` and `ExampleUnitTest.kt` are the default Android Studio stubs.
|
|
- [ ] **GlobalState.kt** — Legacy singleton used by Xposed hooks; may be removable now.
|
|
- [ ] **`Dog` utility** — Thin wrapper over `android.util.Log`.
|
|
|
|
## Module Projects (outside `app/`)
|
|
|
|
| Directory | Purpose |
|
|
|-----------|---------|
|
|
| `root-module/` | KernelSU module packaging (HAL-level camera hook) |
|
|
| `zygisk-module/` | Zygisk module packaging (injection-based) |
|
|
| `native/` | C++ native code for camera HAL hooks |
|
|
| `docs/` | Documentation assets |
|
|
| `camswapper-kernelsu-v2.zip` | Prebuilt KernelSU module |
|
|
| `camswapper-zygisk-v2.zip` | Prebuilt Zygisk module |
|
|
|
|
## Build Commands
|
|
|
|
```bash
|
|
# Build modern flavor (VirtualDeviceManager + system APIs)
|
|
./gradlew :app:assembleModernDebug
|
|
|
|
# Build legacy flavor (no system APIs)
|
|
./gradlew :app:assembleLegacyDebug
|
|
|
|
# Build with native C++ compilation
|
|
./gradlew :app:buildNative
|
|
|
|
# Build release
|
|
./gradlew :app:assembleModernRelease
|
|
```
|