⚠️ This app writes vehicle settings. Use it only while parked and at your own risk. Read DISCLAIMER.md before installing. Incorrect settings may affect vehicle behaviour. MG and MG4 are third-party marks used only to identify compatibility; this independent project is not affiliated with or approved by SAIC Motor or MG Motor.
EVProfile is a system-level Android Automotive OS app for the head unit of MG4 electric vehicles equipped with the SAIC MT2712 SoC. It gives direct, unified access to driving settings that are unavailable, or poorly accessible, through the stock interface: drive mode, regenerative braking, heated seats and steering wheel, ADAS options and steering wheel shortcuts, saved as profiles you apply in one tap, at vehicle start or when a paired Bluetooth device connects.
It communicates with the vehicle through the proprietary SAIC SDK, accessing Android
Automotive services (CarPropertyManager, CarHvacManager) as well as low-level services
exposed by the vehicle. It reads and writes the vehicle through the shared
EVHardware layer.
Important: this application requires system privileges (
sharedUserId="android.uid.system") and must be signed with the ROM's platform key. It cannot run on a standard unlocked device.
EVProfile is one app of EVSuite, a family of independent, offline-first apps for the MG4 head unit (Android Automotive OS 9). Each app installs on its own — pick only what you need. User guides and install instructions: https://malys.github.io/EVSuite/.
Discover the rest of the suite:
- Part of EVSuite
- How it works
- Requirements
- Features
- Hardware Layers
- Profile System
- User Interface
- Required Permissions
- Install
- Building
- Project documents
- Security
- Contributing
- Legal
- Credits
┌──────────────────────────────────────────────────────┐
│ UI LAYER │
│ MainActivity ─── NavController ─── Fragment Host │
│ ┌─────────────┐ ┌──────────────┐ ┌─────────────┐ │
│ │ Dashboard │ │ Profiles │ │ Settings │ │
│ └─────────────┘ └──────────────┘ └─────────────┘ │
└──────────────────────────────────────────────────────┘
│
┌──────────────────────────────────────────────────────┐
│ BUSINESS LOGIC │
│ ProfileManager ─ ProfileApplier ─ FirmwareInfo │
└──────────────────────────────────────────────────────┘
│
┌──────────────────────────────────────────────────────┐
│ HARDWARE ABSTRACTION (EVHardware) │
│ Katman1 (Car API) → Katman2 (Binder) → Katman4 │
│ (ADAS / SWI133 / SWI68) │
└──────────────────────────────────────────────────────┘
│
┌──────────────────────────────────────────────────────┐
│ SYSTEM SERVICES & BOOT │
│ EVProfileService ───── BootReceiver │
└──────────────────────────────────────────────────────┘
Vehicle boot
│
▼
BootReceiver.onReceive()
│
▼
EVProfileService.onCreate()
└─ EVHardware.init()
└─ Katman1 / Katman4 service discovery
└─ Apply default profile (if enabled)
│
▼
MainActivity (UI)
└─ Firmware detection (SWI133 / SWI68)
└─ Top bar setup
└─ Navigate to DashboardFragment
| Item | Value |
|---|---|
| Target vehicle | MG4 Electric (SAIC) |
| OS | Android Automotive 9+ (API 28+) |
| SoC | SAIC MT2712 |
| Screen resolution | 1280 × 480 (forced landscape) |
| Firmware SWI133 | Compatible ✅ |
| Firmware SWI131 | Compatible ✅ |
| Firmware SWI132 | Compatible ✅ |
| Firmware SWI68 | Compatible ✅ |
| Firmware SWI69 | Compatible ✅ |
| Firmware SWI165 | Compatible ✅ |
| UNKNOWN firmware | Forced SWI133/SWI132/SWI68/SWI69/SWI131/SWI165 mode available |
- Drive mode: ECO / NORMAL / SPORT / SNOW / CUSTOM
- Regenerative braking: Off / Low / Medium / High / Adaptive / One Pedal
- Heated steering wheel: On / Off
- Heated seats (left & right): Off / Level 1 / 2 / 3
- SWI133: Off / Speed Limiter / Auto / ACC / ICA + overspeed alert / speed limit change alert
- SWI68: Disable / ACC / TJA + audible warning On / Off
- SWI69 / SWI131: Forward Collision Warning (AEB) — On / Off + mode Alert only / Alert + Emergency Braking
- Configure 4 steering wheel buttons (left/right side buttons)
- Available actions: Drive mode / Regeneration / ADAS / Open app
- Enable/disable shortcuts with a warning dialog
- Door-open volume (SWI132/133): lowers media volume when a front door opens, configurable target level, trigger doors and restore-on-close
- Save up to 5 custom profiles
- Instant one-tap profile application
- Automatic default profile application on vehicle startup
- Automatic profile application on a paired Bluetooth device connecting
- Profile backup to vehicle-persistent storage — survives app uninstall/reinstall
- Language selection (English / German / Spanish / Portuguese / Italian)
- Enable/disable automatic profile application
- Unstable only — OTA: GitHub pre-release check + verified APK download
- Unstable only — APK cleanup: removes old
EVProfile*.apkfiles from Downloads - "About" dialog showing app version, firmware version, and GitHub QR code
EVHardware is organized into 4 access layers, from highest to lowest level, with automatic fallback on failure.
Primary layer. Uses official Android Automotive APIs:
CarPropertyManager→ drive modes, regeneration, one-pedalCarHvacManager→ seat heating, steering wheel heating
The connection is initialized via reflection on Car.createCar() with multiple overloads tried in sequence. Pending actions are queued and executed once the service is ready, with exponential backoff retry (2 s → 60 s).
Falls back to ServiceManager.getService("vehiclesetting") with direct binderTransact() calls. Usually blocked by SELinux in production builds.
Dedicated layer for ADAS functions, dynamically loaded according to the detected firmware generation:
| Firmware | Service | Mechanism |
|---|---|---|
| SWI133 | VehiclePropertyManager |
Loaded from the launcher APK via ClassLoader + reflection on mIVehiclePropertyService. Uses getMixProperty() / setMixProperty() |
| SWI68 | VehicleSettingManager |
Static singleton loaded via reflection. Uses setAccTjaMode() / setLaneKeepingWarningSound() |
| SWI69 / SWI131 | VehicleSettingManager |
Same singleton as SWI68. Uses setFcwState() / getFcwState() / setFcwAutoBrakeMode() / setFcwSensitivity() for AEB. Values confirmed empirically on real hardware: setFcwState(1) = DISABLE, setFcwState(2) = ENABLE. |
| SWI165 | VehicleSettingManager |
Same SDK as SWI68 (com.saicmotor.sdk.vehiclesettings). ADAS via setAccTjaMode(). AEB via setAutoEmergencyBraking(1/2) as the main toggle + setFcwAlarmMode(1/2) + setFcwAutoBrakeMode(1/2). Values: 1=OFF, 2=ON. |
// util/FirmwareInfo.kt
val gen = FirmwareInfo.getGeneration() // Reads ro.build.mt2712.version
// → Gen.SWI133 | Gen.SWI68 | Gen.UNKNOWNThe result is cached and used throughout the app to branch firmware-specific code paths.
data class DrivingProfile(
val id: String, // Unique UUID
val name: String, // Display name
val driveMode: DriveMode, // ECO / NORMAL / SPORT / SNOW / CUSTOM
val regenLevel: RegenLevel, // OFF / LOW / MEDIUM / HIGH / ADAPTIVE / ONE_PEDAL
val steeringHeat: Boolean,
val seatHeatLeft: Int, // 0–3
val seatHeatRight: Int, // 0–3
// SWI133 only:
val overspeedAlarm: Boolean,
val speedLimitTone: Boolean,
val adasMode: Int, // 0=Off 1=Limiter 2=Auto 3=ACC 4=ICA
// SWI68 only:
val soundWarning: Boolean,
val swi68AdasMode: Int // Swi68Mode.OFF / ACC / TJA
)Profiles are serialized to JSON via Gson and stored in SharedPreferences. Maximum 5 profiles per device.
ProfileApplier.apply() executes hardware calls in the following order on Dispatchers.IO:
- Drive mode (fast — binder call)
- Regen level (fast — binder call)
- Heated steering wheel (~2 s — state confirmation polling)
- Left seat heating (~7 s — toggle polling)
- Right seat heating (~7 s — toggle polling)
- Wait for Katman4 → ADAS (firmware-dependent)
The app uses a NavController with 3 destinations:
DashboardFragment (start)
├──► ProfileFragment (PROFILS button — toggle)
└──► SettingsFragment (SETTINGS button — toggle)
A second press on PROFILES or SETTINGS closes the view and returns to the dashboard.
2-row layout (2:1 weight ratio) optimized for 1280×480:
- Top row (2/3 height): Drive mode | Regeneration | ADAS
- Bottom row (1/3 height): Climate (steering + seats) | Alerts
| Token | Hex | Usage |
|---|---|---|
dash_bg |
#0C0C0E |
App background |
dash_card |
#141416 |
Cards |
dash_section |
#1C1C1F |
Inner sections |
dash_border |
#2A2A2E |
Borders |
dash_accent |
#38BDF8 |
Active selection (blue) |
dash_eco |
#22C55E |
ECO mode (green) |
dash_warn |
#F59E0B |
SPORT mode (amber) |
dash_danger |
#F43F5E |
Delete / danger actions |
| Permission | Reason |
|---|---|
FOREGROUND_SERVICE |
Persistent foreground service for auto-apply |
WAKE_LOCK |
Prevents sleep during settings application |
RECEIVE_BOOT_COMPLETED |
Auto-start on vehicle boot |
BLUETOOTH (Android 11 and below) |
Match paired devices to automatic profiles |
BLUETOOTH_CONNECT (Android 12+) |
Match paired devices to automatic profiles |
CAR_POWERTRAIN |
Drive mode and regeneration control |
CONTROL_CAR_CLIMATE |
Seat and steering wheel heating control |
CAR_VENDOR_EXTENSION |
SAIC proprietary extensions |
CAR_ENERGY |
Battery / powertrain information |
INTERNET (unstable only) |
Rolling pre-release check and verified APK download |
ACCESS_NETWORK_STATE (unstable only) |
Warn before downloading outside Wi-Fi |
Download the APK from the releases page and install it from a USB drive through the AAOS settings. The MG4 head unit has no visible way to open Settings or install an APK; the known route in (via the on-screen keyboard) is:
- Open any app with a text field and tap it to raise the on-screen keyboard.
- Long-press the comma
,(or the@) key on the keyboard. - Tap "Language settings".
- Tap the search icon in the top bar and type
backup. It opens an empty page — now press the back arrow, and you land in Android's Settings panel. - Enable Developer options, and turn on "Install unknown apps" (unknown sources).
- In Settings, search
storage— you now have access to internal storage and the USB key. Navigate to the APK and tap it to install.
The application must be signed with the ROM's system key. On a development system:
adb push app-debug.apk /sdcard/
adb shell pm install -r --system /sdcard/app-debug.apkOn a production ROM, the APK must be included in the system build or installed through an OEM-specific mechanism.
⚠️ You are enabling developer options and sideloading on a car. Do this parked, and only with an APK you trust. See DISCLAIMER.md.
mise run build # stable debug APK
mise run test # JVM unit tests for both channels
mise run lint # Android lint
mise run check # what CI runs (permission drift gate + tests)
mise run release # both release APKs (R8 on); validate on a vehicle before publishingWithout mise, with Android Studio Hedgehog (2023.1) or later, JDK 17+ and Android SDK API 34:
# Using Android Studio's bundled JDK
JAVA_HOME="/path/to/Android Studio/jbr" ./gradlew assembleStableDebug
# Tester channel with OTA:
JAVA_HOME="/path/to/Android Studio/jbr" ./gradlew assembleUnstableDebugOutput APK location:
app/build/outputs/apk/debug/app-debug.apk
app/src/main/java/com/evsuite/profile/
EVApp.kt Application — night mode, locale
MainActivity.kt Main activity, top bar, navigation
profile/ ProfileManager (SharedPreferences + Gson), ProfileApplier, backup, climate plan
automation/ Automatic-application decision and settings
bluetooth/ Paired-device profile matching
shortcut/ Steering wheel shortcut actions
api/ Profile control service (AIDL in app/src/main/aidl)
ui/ Dashboard, Profile, Settings, Shortcuts, Audio, Console and legacy fragments
service/ EVProfileService (foreground, boot + auto-apply), profile overlays
receiver/ BootReceiver
util/ LocaleHelper, ThemeHelper, QrCode
app/src/stable/ app/src/unstable/ per-channel sources (update code is unstable only)
app/src/test/ app/src/testUnstable/ JVM unit tests
Hardware access lives in the shared EVHardware
submodule (EVHardware/).
- Stable (
com.evsuite.profile): offline APK, no updater code and no network permission. Install and update it manually from a tagged GitHub Release. - Unstable (
com.evsuite.profile.unstable): rolling pre-release that can coexist with stable. It checks theunstableGitHub pre-release, validates HTTPS origins and the APK signing certificate, then requires an explicit user installation.
Migration: the former online package upgrades in place to stable because it used the same
application ID. The former com.evsuite.profile.offline package is a separate installation;
back up its profiles, install stable, restore them, then uninstall the legacy package.
| Document | What it covers |
|---|---|
| DESIGN.md | The EVSuite design system — colour, type, touch targets, icons |
| AGENTS.md | Context for AI agents working in this repository |
| CONTRIBUTING.md | How to build, test and submit a change |
| SECURITY.md | Threat model and vulnerability disclosure |
| DISCLAIMER.md | Vehicle-safety disclaimer — read before installing |
| CHANGELOG.md | Release history |
| LICENSE | Licence text |
See SECURITY.md for the threat model and how to report a vulnerability privately.
Read CONTRIBUTING.md before opening a pull request. In short: this code runs in a moving vehicle, so changes stay small, carry tests, and say in the diff what would break without them. Anything touching the interface follows DESIGN.md.
Licensed under the MIT License — see LICENSE and LICENSE.md. Read DISCLAIMER.md before installing: this application writes to vehicle settings, and wrong settings can affect how the car behaves. Not affiliated with SAIC Motor or MG.
Made with ❤ by SliDeeN and Claude AI.
Based on DriveHub Dort, developed by Merth4n and hotboy_ist.
Special thanks to confor1max for extensive SWI68 firmware testing.