# Building ISO Droid This document contains technical information for building and understanding ISO Droid. ## Building from Source ### Prerequisites Before building the app, you need to obtain the `isodrive` binary: 1. The `isodrive` binary is compiled from [nitanmarcel/isodrive](https://github.com/nitanmarcel/isodrive) 2. Place the compiled binary at `app/src/main/assets/bin/isodrive` before building 3. The binary must be executable and compiled for the appropriate Android architecture (ARM64 recommended for modern devices) ### Build Steps ```bash # Clone the repository git clone https://github.com/sargit/ISODroid.git cd ISODroid # Ensure isodrive binary is present ls -la app/src/main/assets/bin/isodrive # Build the debug APK ./gradlew assembleDebug # Or build the release APK (requires signing configuration) ./gradlew assembleRelease ``` The compiled APK will be located at: - Debug: `app/build/outputs/apk/debug/app-debug.apk` - Release: `app/build/outputs/apk/release/app-release.apk` ## Technical Architecture ### Overview ISO Droid is built using modern Android development practices: - **Language**: Kotlin - **UI Framework**: Jetpack Compose (Material 3) - **Architecture**: MVVM (Model-View-ViewModel) - **Minimum SDK**: 26 (Android 8.0) - **Target SDK**: 34 (Android 14) ### Core Components #### 1. IsoDriveManager (`isodrive/IsoDriveManager.kt`) The central component that interfaces with the `isodrive` binary: - **Binary Extraction**: Copies the bundled `isodrive` binary from assets to app-specific storage - **Permission Management**: Ensures the binary has executable permissions (`chmod 755`) - **Command Execution**: Wraps all isodrive commands (mount, unmount, status checks) - **Device Support Detection**: Checks for configfs or sysfs USB gadget support ```kotlin // Example: Mounting an ISO isoDriveManager.mount( filePath = "/sdcard/isodrive/ubuntu.iso", mountType = MountType.CDROM, readOnly = true ) ``` #### 2. RootManager (`root/RootManager.kt`) Handles all root access operations: - **Root Detection**: Checks for available root methods (Magisk, KernelSU, APatch, su binary) - **Cached Status**: Stores root grant status to avoid repeated prompts - **Command Execution**: Executes shell commands with root privileges - **Error Handling**: Captures stdout, stderr, and exit codes ```kotlin val result = RootManager.executeCommand("isodrive status") if (result.isSuccess) { // Parse output } ``` #### 3. USB Gadget Subsystem ISO Droid supports both modern and legacy USB gadget interfaces: **ConfigFS (Modern - Preferred)** - Path: `/config/usb_gadget/` - More flexible and feature-rich - Standard on Android 8.0+ devices **SysFS (Legacy)** - Path: `/sys/class/android_usb/` - Older interface - Fallback for older devices The `isodrive` binary automatically detects and uses the appropriate interface. #### 4. Mount Types **Mass Storage Mode** - Exposes the file as a USB mass storage device - Supports read-write mode for IMG files - Host PC sees it as a USB flash drive - Can be formatted with any filesystem (FAT32, NTFS, exFAT, ext4) **CD-ROM Mode** - Exposes the file as a virtual CD/DVD drive - Always read-only - Ideal for bootable ISOs - Host PC sees it as an optical drive ### UI Architecture #### Jetpack Compose All UI is built using Jetpack Compose with Material 3: - **Declarative**: UI is a function of state - **Reactive**: Automatically updates when state changes - **Type-safe**: Compile-time checking for UI code #### Key Screens 1. **MainScreen** (`ui/screens/MainScreen.kt`) - File browser with OS icon detection - Mount/unmount operations - Status card showing current mount state 2. **DownloadsScreen** (`ui/screens/DownloadsScreen.kt`) - Curated list of OS download links - Grouped by category (Linux, BSD, Windows, Recovery) 3. **SettingsScreen** (`ui/screens/SettingsScreen.kt`) - Root permission status - Notification permission (Android 13+) - Default ISO directory configuration - App version and build information #### State Management Uses Kotlin StateFlow for reactive state: ```kotlin data class MainUiState( val isLoading: Boolean = true, val hasRoot: Boolean? = null, val isSupported: Boolean? = null, val mountStatus: MountStatus = MountStatus.UNMOUNTED, val isoFiles: List = emptyList(), // ... ) ``` ### OS Icon Detection Dynamic icon matching system: 1. **Icon Storage**: SVG files in `app/src/main/assets/osicons/` 2. **Dynamic Loading**: Icons are loaded at runtime via `AssetManager` 3. **Fuzzy Matching**: Filename matching algorithm: - Converts filename to lowercase - Checks for icon name presence (e.g., "ubuntu" in "ubuntu-22.04-desktop.iso") - Prioritizes longer matches (e.g., "linuxmint" over "linux") - Prefers earlier position in filename 4. **Rendering**: Uses Coil image library with SVG decoder 5. **Badge System**: Small circular badge shows file type (ISO/IMG) ### File Operations #### Creating IMG Files ```kotlin // User specifies size (e.g., 4 GB) val sizeBytes = 4L * 1024 * 1024 * 1024 // Creates sparse file using dd with progress reporting dd if=/dev/zero of=/sdcard/isodrive/custom.img bs=1M count=4096 // Progress events are emitted via CreateImgEventBus ``` #### Rename/Delete Protection - Files currently mounted cannot be renamed or deleted - UI shows warning message and disables actions - Prevents accidental data corruption ### Notifications Persistent notification while mounted: - **Information**: Shows mounted filename and mount type - **Quick Actions**: Unmount button in notification - **Foreground Service**: Ensures notification stays visible - **Auto-dismiss**: Notification removed after unmount ### Data Persistence Uses Android DataStore (Preferences): ```kotlin // Saves user preferences dataStore.edit { preferences -> preferences[KEY_ISO_DIRECTORY] = "/sdcard/custom/path" } ``` Stores: - ISO directory path - Root grant status (from setup wizard) - App settings ### Event Bus System Decoupled communication using Kotlin Flow: ```kotlin // Mount events object MountEventBus { private val _events = MutableSharedFlow() val events: SharedFlow = _events.asSharedFlow() } // Emit event from notification MountEventBus.emit(MountEvent.Unmounted) // Observe in ViewModel MountEventBus.events.collect { event -> when (event) { is MountEvent.Unmounted -> updateUiState() } } ``` ## Dependencies ### Core Libraries - **Jetpack Compose**: Modern declarative UI - **Material 3**: Design system and components - **Lifecycle & ViewModel**: Android Architecture Components - **DataStore**: Modern preference storage - **Coroutines**: Asynchronous programming ### Image Loading - **Coil**: Image loading library - **Coil SVG**: SVG decoder for OS icons ### JSON Parsing - **org.json**: Parsing OS download list (`assets/os.json`) ## Testing ### Manual Testing Checklist - [ ] Root access granted on first launch - [ ] USB gadget support detected correctly - [ ] ISO files mount successfully - [ ] IMG files can be created and mounted - [ ] OS icons display correctly - [ ] Unmount via notification works - [ ] File rename/delete protection when mounted - [ ] Settings persist across app restarts - [ ] Dark mode toggles correctly ### Device Compatibility Tested on: - Modern devices with ConfigFS (Android 8.0+) - Legacy devices with SysFS (older Android versions) - Various root methods (Magisk, KernelSU, APatch) ## Debugging ### Enable Verbose Logging Check logcat for ISO Droid logs: ```bash adb logcat | grep -i "isodroid\|isodrive" ``` ### Common Issues **"Device not supported"** - Check for USB gadget support: `ls /config/usb_gadget/` or `ls /sys/class/android_usb/` - Verify kernel has USB gadget drivers **"Root access required"** - Ensure root manager (Magisk/KernelSU) is installed - Grant root access when prompted - Check `RootManager.hasRoot()` returns true **Mount fails** - Verify file exists and is readable - Check file isn't corrupted - Ensure no other USB modes are active - Try rebooting device ### isodrive Binary Version Check the bundled binary version: ```bash adb shell su /data/data/sh.sar.isodroid/files/isodrive --version ``` ## Contributing ### Code Style - Follow Kotlin coding conventions - Use meaningful variable names - Comment complex logic - Keep functions focused and small ### Pull Requests 1. Fork the repository 2. Create a feature branch 3. Make your changes 4. Test thoroughly on a real device 5. Submit a pull request with description ## License GNU General Public License v3.0 - See [LICENSE](../LICENSE) file for details