Developer referenceRéférence développeurs
How LibreHU works, down to the bytes. Everything here comes from the repositories' code and docs, the analysis of the stock firmware and measurements on the Jancar UJC201; each section links to its source.Comment marche LibreHU, jusqu'à l'octet. Tout ici vient du code et de la doc des dépôts, de l'analyse du firmware d'origine et de relevés sur le Jancar UJC201 ; chaque section renvoie à sa source.
ArchitectureArchitecture
One privileged service owns the hardware; every other app goes through it. Nothing else opens the MCU serial port.Un seul service privilégié tient le matériel ; toutes les autres applis passent par lui. Rien d'autre n'ouvre le port série de la MCU.
core/of LibreHU-service is plain Kotlin/JVM (MCU framing and profiles, Hiworld decoding, OBD formulas, touch maths, bridge crypto) with unit tests runnable without the Android SDK:./gradlew -PcoreOnly :core:test.core/de LibreHU-service est du Kotlin/JVM pur (trames et profils MCU, décodage Hiworld, formules OBD, calculs tactiles, crypto du pont) avec des tests unitaires lançables sans SDK Android :./gradlew -PcoreOnly :core:test.- All apps: Kotlin, Jetpack Compose, minSdk 28 (Android 9), target 35, R8 without obfuscation, one shared public signing key.Toutes les applis : Kotlin, Jetpack Compose, minSdk 28 (Android 9), cible 35, R8 sans renommage, une clé de signature publique commune.
Hardware: Jancar UJC201Matériel : Jancar UJC201
SoC
| ItemÉlément | ValueValeur |
|---|---|
| UnitAutoradio | Jancar UJC201, board JAC_8258C (jancar_board_id A0_AN), sold as SIXWIN, Podofo, Hikity…Jancar UJC201, carte JAC_8258C (jancar_board_id A0_AN), vendu sous SIXWIN, Podofo, Hikity… |
| SoC | Autochips AC8257 (MediaTek MT6761/MT6765 family), ro.product.device=ac8257_demo; 4 × Cortex-A53 (MIDR 410fd034, SMP: Total of 4 processors activated), stock table up to 2.0 GHz; ARM9 "ARM2" for fast reverse before Linux, handed over to Android by BackCar.sendReadyToArm2() (kernel IOCTL_FSC_NOTIFY_APP_READY; until then OVL0 layers 1 and 2 stay with the ARM2 and hardware video is green: sent by hd2cam, and by LibreHU service at start-up since versionCode 134)Autochips AC8257 (famille MediaTek MT6761/MT6765), ro.product.device=ac8257_demo ; 4 × Cortex-A53 (MIDR 410fd034, SMP: Total of 4 processors activated), table d'origine jusqu'à 2,0 GHz ; ARM9 « ARM2 » pour le recul rapide avant Linux, qui rend la main à Android sur BackCar.sendReadyToArm2() (noyau : IOCTL_FSC_NOTIFY_APP_READY ; d'ici là les couches 1 et 2 d'OVL0 restent à l'ARM2 et la vidéo matérielle est verte : envoyé par hd2cam, et par LibreHU service au démarrage depuis la versionCode 134) |
| GPU | PowerVR GE8300 (rgx, DDK 1.10@5130912), OpenGL ES 3.2, no vendor Vulkan driverPowerVR GE8300 (rgx, DDK 1.10@5130912), OpenGL ES 3.2, pas de pilote Vulkan vendor |
| Android | 9 (SDK 28); ro.build.version.release faked to 12 by Jancar; system-as-root (skip_initramfs); SELinux permissive9 (SDK 28) ; ro.build.version.release falsifié en 12 par Jancar ; system-as-root (skip_initramfs) ; SELinux permissif |
| KernelNoyau | Linux 4.9.117+ #72 (stock, 22/01/2026)Linux 4.9.117+ #72 (origine, 22/01/2026) |
| DisplayÉcran | 1280 × 720 panel mounted landscape (persist.sf.hwrotation=90, rotation read from the metazone)Dalle 1280 × 720 montée en paysage (persist.sf.hwrotation=90, rotation lue dans la metazone) |
| TouchTactile | Goodix GT911 (firmware 1060) driven by MediaTek's GT928 driver, input mtk-tpd; calibration matrix /sys/devices/platform/touch/gt9xx_props = A B C D E F Div, x' = (A·x + B·y + C) / DivGoodix GT911 (firmware 1060) piloté par le pilote GT928 de MediaTek, entrée mtk-tpd ; matrice de calibration /sys/devices/platform/touch/gt9xx_props = A B C D E F Div, x' = (A·x + B·y + C) / Div |
| SoundSon | ROHM BD37534 on /dev/i2c-6, address 0x40, driven from Android (no ALSA control)ROHM BD37534 sur /dev/i2c-6, adresse 0x40, piloté depuis Android (aucun contrôle ALSA) |
| ConnectivityConnectivité | MT6631 combo: Wi-Fi (wlan_drv_gen4m), BT, GPS, FM (fmradio_drv, /dev/fm; r146: its 32-bit compat layer passes RESTORE_SEARCH and SETMONOSTERO on, so the end of a scan and the forced mono work from a 32-bit app); no I²C tuner (jancar.radio.id=-1)Combo MT6631 : Wi-Fi (wlan_drv_gen4m), BT, GPS, FM (fmradio_drv, /dev/fm ; r146 : sa couche de compatibilité 32 bits transmet RESTORE_SEARCH et SETMONOSTERO, la fin de balayage et le mono forcé marchent depuis une appli 32 bits) ; pas de tuner I²C (jancar.radio.id=-1) |
| CamerasCaméras | CVBS by the integrated TVD (720 × 576), AHD by Techpoint TP9963 on MIPI CSI-2; HAL ids: USB 0, MIPI 2, CVBS 3, MIPI VC 8CVBS par le TVD intégré (720 × 576), AHD par Techpoint TP9963 en MIPI CSI-2 ; ids de la HAL : USB 0, MIPI 2, CVBS 3, MIPI VC 8 |
| MCU | HK32C030C8T7 (Cortex-M0, STM32F030 clone, 64 KB flash, 8 KB RAM, 48 MHz), firmware JCST_AC8257_8T7-2024.08.09HK32C030C8T7 (Cortex-M0, clone STM32F030, 64 Ko flash, 8 Ko RAM, 48 MHz), firmware JCST_AC8257_8T7-2024.08.09 |
GPIO, I²C, inputs
/dev/gpios_ioctl (Jancar module, reimplemented in the LibreHU kernel): ioctl(fd, cmd, &gpio), the argument points to the GPIO number. 0x6B00 high, 0x6B01 low, 0x6B02 input, 0x6B03 read (level returned). Reading switches the pin to input: never read an output./dev/gpios_ioctl (module Jancar, réimplémenté dans le noyau LibreHU) : ioctl(fd, cmd, &gpio), l'argument pointe vers le numéro de GPIO. 0x6B00 haut, 0x6B01 bas, 0x6B02 entrée, 0x6B03 lecture (niveau renvoyé). Lire passe la broche en entrée : ne jamais lire une sortie.
| GPIO | RoleRôle | Direction / logicSens / logique |
|---|---|---|
| 2 | reverse gear (CCD)marche arrière (CCD) | input, active lowentrée, actif bas |
| 7 / 6 | left / right turn signalclignotant gauche / droit | input, active lowentrée, actif bas |
| 5 | screen backlightrétroéclairage écran | output, 1 = onsortie, 1 = allumé |
| 166 | amplifier mutesourdine ampli | output, 1 = mutedsortie, 1 = muet |
| 110 | radio antenna power (with MCU 43)alimentation antenne radio (avec MCU 43) | outputsortie |
| 167 / 165 | CVBS video switch SW1 / SW2: AV → (0,0), front camera → (1,0), rear camera → (1,1)commutateur vidéo CVBS SW1 / SW2 : AV → (0,0), caméra avant → (1,0), caméra arrière → (1,1) | outputsortie |
| 112 | tuner power / resetalimentation / reset tuner | output, 1 at bootsortie, 1 au démarrage |
| 174 | USB hub resetreset du hub USB | 0 for 20 ms then 10 pendant 20 ms puis 1 |
- I²C:
/dev/i2c-0..6,I2C_SLAVE_FORCE(0x706) thenI2C_RDWR(0x707).i2c-1andi2c-6are labelledmfi_auth_device(CarPlay MFi chip).I²C :/dev/i2c-0..6,I2C_SLAVE_FORCE(0x706) puisI2C_RDWR(0x707).i2c-1eti2c-6sont étiquetésmfi_auth_device(puce MFi de CarPlay). - Inputs:
mtk-kpd(event0, SoC keys),gpio_ir_recv(event1, IR),mtk-tpd(event2, touch); found by name in/proc/bus/input/devices. Front panel keys are touch areas outside the display, read raw from the event device (struct input_event, 24 bytes on arm64).Entrées :mtk-kpd(event0, touches du SoC),gpio_ir_recv(event1, IR),mtk-tpd(event2, tactile) ; trouvées par nom dans/proc/bus/input/devices. Les touches de façade sont des zones tactiles hors de l'affichage, lues brutes sur le périphérique d'entrée (struct input_event, 24 octets en arm64). - Device permissions on stock:
/dev/gpios_ioctl0777,/dev/i2c-*0777 system,/dev/ttyS1..30666 system.Droits sur la ROM d'origine :/dev/gpios_ioctl0777,/dev/i2c-*0777 system,/dev/ttyS1..30666 system.
Partitions, AVBPartitions, AVB
recoveryis exactly 32 MiB; the bootloader message (BCB) lives onpara(no/misc);bootis system-as-root.recoveryfait exactement 32 Mio ; le message du bootloader (BCB) est surpara(pas de/misc) ;bootest en system-as-root.- The stock
vbmetachainsrecovery(Jancar key, public key digest0aa0987116fc792f36bc909b0a4d530413f02a54, rollback location 1): a custom recovery must carry the stock recovery's vbmeta footer, else Android bootloops. TWRP's post-processing does that.Levbmetad'origine chaînerecovery(clé Jancar, empreinte de clé publique0aa0987116fc792f36bc909b0a4d530413f02a54, rollback location 1) : un recovery modifié doit porter le pied vbmeta du recovery d'origine, sinon Android boucle. Le post-traitement de TWRP le fait. fastboot bootdoes not work on this unit: flashrecovery. Init does not rewrite recovery at boot (flash_recoverycommented out in/system/init.rc).fastboot bootne marche pas sur cet autoradio : flasherrecovery. Init ne réécrit pas le recovery au démarrage (flash_recoverycommenté dans/system/init.rc).- Metazone (
/dev/mtz, Autochips): rotation, camera format, panel parameters. LibreHU reads it and writes only the camera format, like Jancar.Metazone (/dev/mtz, Autochips) : rotation, format caméra, paramètres de dalle. LibreHU la lit et n'écrit que le format caméra, comme Jancar.
MCU protocol (JAC_V1)Protocole MCU (JAC_V1)
Reverse engineered from the firmware (jacmcu.bin) and the stock Android stack; full analysis in MCU-tools-app/docs/mcu_firmware.md.Retrouvé à partir du firmware (jacmcu.bin) et de la pile Android d'origine ; analyse complète dans MCU-tools-app/docs/mcu_firmware.md.
FrameTrame
EE FA LEN CMD D0 .. Dn-1 CS LEN = n + 1 CS = (EE + FA + LEN + CMD + D0 + .. + Dn-1) & 0xFF
examplesexemples: EE FA 02 1F 01 0A (PC_READY) EE FA 03 F0 00 00 DB (ACC?ACC ?)
/dev/ttyS1↔ MCU USART2, 115200 8N1. Data ≤ 130 bytes; the MCU drops a frame after 1 s without a byte./dev/ttyS1↔ USART2 de la MCU, 115200 8N1. Données ≤ 130 octets ; la MCU abandonne une trame après 1 s sans octet.- Every valid frame except
C0is acknowledged withC0 <cmd> <d0> <d1>. LibreHU resends1F 08 01 F1 80every 500 ms, 5 times, until acknowledged.Toute trame valide saufC0est acquittée parC0 <cmd> <d0> <d1>. LibreHU renvoie1F 08 01 F1 80toutes les 500 ms, 5 fois, jusqu'à l'acquittement. - Long frames (CAN data,
LEN≥0x80) must be read with an unsigned length: the stock Android stack crashes on them.Les trames longues (données CAN,LEN≥0x80) doivent être lues avec une longueur non signée : la pile Android d'origine plante dessus. - At start, send
1F 01(PC_READY): the MCU answers ACC (00), version (0A, +200 ms), date and time (09, +400 ms). No heartbeat to keep.Au démarrage, envoyer1F 01(PC_READY) : la MCU répond ACC (00), version (0A, +200 ms), date et heure (09, +400 ms). Aucun battement de cœur à entretenir.
SoC → MCU
| Cmd | DataDonnées | EffectEffet |
|---|---|---|
01 | [type ..] | type 3..5: delayed power-off (15 min max); type 2 (sent by stock Android at ACC off) ignoredtype 3..5 : extinction différée (15 min au plus) ; type 2 (envoyé par Android d'origine à la coupure) ignoré |
08 | [0/1] | mute (PA0)sourdine (PA0) |
09 | [0, yy/100, yy%100, month, day] · [1, h, m, s] | set the RTCrègle la RTC |
0E | [0, 0, 0] | reset the SoC (power cycle of its rails), works when Android hangsreset du SoC (coupure de ses rails), marche quand Android est bloqué |
0F | [sub ..] | configuration, see belowconfiguration, voir ci-dessous |
10 | bytesoctets | copied as is to USART1 (CAN box)recopiés tels quels sur USART1 (boîtier CAN) |
11 / 21 | [2] / otherautre | steering wheel / front panel key learning start / enddébut / fin d'apprentissage des touches volant / façade |
1F | [01] | PC_READY |
43 | [0/1] | radio antenna power (PF6)alimentation antenne radio (PF6) |
44 | [0/1] | REM output, external amplifier (PB3)sortie REM, ampli externe (PB3) |
80 | [00 ..] | jump to the MCU bootloader (firmware update)passage au bootloader de la MCU (mise à jour du firmware) |
F0 | [qq, 00] | query: 00 ACC, 04 handbrake, 08 mute, 09 clock, 0A version, 0B lights, 0D backlight, 43 radio, 0F 07 optionrequête : 00 ACC, 04 frein à main, 08 sourdine, 09 heure, 0A version, 0B feux, 0D rétroéclairage, 43 radio, 0F 07 option |
F1 | [n] | standby duration n × 420 min (0 = power off)durée de veille n × 420 min (0 = extinction) |
0F sub-commands: 00 [i] CAN USART speed (9600…460800, i = 0..6, default 38400) · 02 [p] / 03 [p] PWM brightness 5..100 % (PA8, PB14) · 04 [type, R, G, B, mode] front LEDs (R/G/B 0..99, mode 1 auto cycle, 2 manual, 3 semi-auto) · 06 knob parameters · 08 [type] panel type · 0A [i] high threshold 16..20 V · 0B [i] low threshold 9 / 9.5 / 10 V. Settings are stored in a flash page (0x08002000) written 3 s after the last change.Sous-commandes 0F : 00 [i] vitesse de l'USART du CAN (9600…460800, i = 0..6, 38400 par défaut) · 02 [p] / 03 [p] luminosité PWM 5..100 % (PA8, PB14) · 04 [type, R, V, B, mode] LED de façade (R/V/B 0..99, mode 1 cycle auto, 2 manuel, 3 semi-auto) · 06 paramètres de la molette · 08 [type] type de façade · 0A [i] seuil haut 16..20 V · 0B [i] seuil bas 9 / 9,5 / 10 V. Les réglages sont gardés dans une page de flash (0x08002000) écrite 3 s après le dernier changement.
MCU → SoC
| Cmd | DataDonnées | WhenQuand |
|---|---|---|
00 | [acc] | after PC_READY, ignition change, queryaprès PC_READY, changement du contact, requête |
04 | [hb] | handbrake change, querychangement du frein à main, requête |
09 | [0, 20, yy, mm, dd] · [1, h, m, s] | +400 ms after PC_READY, query+400 ms après PC_READY, requête |
0A | 32 ASCII bytes, e.g. JCST_AC8257_8T7-2024.08.09_12:5932 octets ASCII, ex. JCST_AC8257_8T7-2024.08.09_12:59 | +200 ms after PC_READY+200 ms après PC_READY |
0B | [ill] | lights changechangement des feux |
10 | ≤ 128 bytes≤ 128 octets | CAN box data, ~20 ms after its last bytedonnées du boîtier CAN, ~20 ms après son dernier octet |
20 / 30 | [ch, v1, v2, v3, v4] | key (30 while learning): channel 5-6 steering wheel (3 ADC readings, released = FF FF FF), 3-4 front panel, 2 knobtouche (30 pendant l'apprentissage) : canal 5-6 volant (3 mesures ADC, relâché = FF FF FF), 3-4 façade, 2 molette |
C0 | [cmd, d0, d1] | ACK |
Power statesÉtats d'alimentation
| StateÉtat | Behaviour (100 ms task)Comportement (tâche 100 ms) |
|---|---|
| 0 | off, waiting for ACC; ACC and voltage below the high threshold → powers the SoCéteint, attente du contact ; contact et tension sous le seuil haut → alimente le SoC |
| 1 | cold start: SoC rail at +1.5 s, waits for 1F 01 up to 15 min; warm start (from standby): wake pulse, waits 10 sdémarrage à froid : rail du SoC à +1,5 s, attend 1F 01 jusqu'à 15 min ; à chaud (sortie de veille) : impulsion de réveil, attend 10 s |
| 2 | running; ACC lost (1 s filter) or overvoltage for 3 s → sends 00 00marche ; contact perdu (filtre 1 s) ou surtension 3 s → envoie 00 00 |
| 3 | ACC off: ACC back → 00 01; else SoC cut after 15 s (40 s if not running)contact coupé : retour → 00 01 ; sinon SoC coupé après 15 s (40 s s'il ne tourne pas) |
| 4 | standby / off: with an F1 duration, SoC kept suspended, RTC wake-ups, full off below the low thresholdveille / arrêt : avec une durée F1, SoC gardé en suspension, réveils RTC, arrêt complet sous le seuil bas |
LibreHU-service exposes two states: FLAG_ACC (the ignition line, at once) and FLAG_POWERED (follows ACC but drops only after the engine-start delay, 0..10 s). Screen, sound, amplifier and standby follow POWERED. Source: docs/power.md.LibreHU-service expose deux états : FLAG_ACC (le fil de contact, tout de suite) et FLAG_POWERED (suit le contact mais ne retombe qu'après le délai de démarrage moteur, 0..10 s). Écran, son, ampli et veille suivent POWERED. Source : docs/power.md.
MCU profiles (JSON)Profils MCU (JSON)
The protocol is data, not code: jancar-jac-v1 runs on a native implementation, any other profile on a generic engine; a unit test checks both produce the same frames for the Jancar profile. Import / export in Advanced → MCU.Le protocole est une donnée, pas du code : jancar-jac-v1 tourne sur une implémentation native, tout autre profil sur un moteur générique ; un test unitaire vérifie que les deux produisent les mêmes trames pour le profil Jancar. Import / export dans Avancé → MCU.
{
"format": "librehu-mcu-profile", "version": 1, "id": "jancar-jac-v1",
"serial": { "port": "/dev/ttyS1", "baud": 115200 },
"frame": { "header": "EE FA", "lengthOffset": 2, "lengthCounts": "cmd_data", "lengthAdd": 0, "cmdOffset": 3,
"checksum": "sum8", "checksumFrom": 0, "checksumAdjust": 0, "maxData": 130 },
"ack": { "cmd": "C0", "ackedByte": 0, "needs": ["01", "08", "1F", "80", "F1"], "timeoutMs": 500, "tries": 5 },
"inputs": [ { "signal": "acc", "cmd": "00" },
{ "signal": "date", "cmd": "09", "match": { "0": "00" }, "fields": { "yearHi": 1, "yearLo": 2, "month": 3, "day": 4 } } ],
"outputs": { "pcReady": "1F 01", "muteOn": "08 01", "muteOff": "08 00",
"date": "09 00 {yearHi} {yearLo} {month} {day}", "can": "10 {data}" },
"board": { "reverseGpio": 2, "turnLeftGpio": 7, "turnRightGpio": 6, "backlightGpio": 5, "ampMuteGpio": 166,
"antennaGpio": 110, "audioChip": "bd37534", "audioBus": 6, "audioAddress": "40" }
}
lengthCounts:data,cmd_data,frameorafter_length;checksum:none,sum8,xor8,twos8, fromchecksumFrom, pluschecksumAdjust.lengthCounts:data,cmd_data,frameouafter_length;checksum:none,sum8,xor8,twos8, depuischecksumFrom, pluschecksumAdjust.- Input signals:
acc,handbrake,headlight,backlight,reverse(byte,mask,invert),version,date,time,key,can; first match wins.Signaux d'entrée :acc,handbrake,headlight,backlight,reverse(byte,mask,invert),version,date,time,key,can; le premier qui correspond gagne. - Outputs:
pcReady,muteOn/Off,ampOn/Off,antennaOn/Off,date,time,can,queryClock,watchdogOff,resetSoc,sleepTimer({units}); a missing output is not sent.boardGPIOs may benull(not wired).Sorties :pcReady,muteOn/Off,ampOn/Off,antennaOn/Off,date,time,can,queryClock,watchdogOff,resetSoc,sleepTimer({units}) ; une sortie absente n'est pas envoyée. Les GPIO deboardpeuvent valoirnull(non câblé).
CAN boxBoîtier CAN
The CAN box sits on CAN-H / CAN-L and translates the car's bus into its own messages, configured per car (Renault: Hiworld protocol LNP002). The MCU only relays bytes (USART1, command 10, both ways); LibreHU rebuilds them, decodes them with the car profile and answers the box as its own app does (without that a Hiworld box sends no steering wheel key). Frames of each family, from Hiworld's Car Info 1.0.3991 (read, never run):Le boîtier CAN est branché sur CAN-H / CAN-L et traduit le bus de la voiture en ses propres messages, réglés par voiture (Renault : protocole Hiworld LNP002). La MCU ne fait que relayer les octets (USART1, commande 10, dans les deux sens) ; LibreHU les reconstitue, les décode avec le profil de la voiture et répond au boîtier comme son appli d'origine (sans cela un boîtier Hiworld n'envoie aucune touche du volant). Trames de chaque famille, d'après Car Info 1.0.3991 de Hiworld (lue, jamais exécutée) :
box | FrameTrame | ChecksumSomme | Head unit answersL'autoradio répond | Makers (also accepted as box)Fabricants (acceptés aussi comme box) |
|---|---|---|---|---|
hiworld | 5A A5 LEN CMD D… CS | ((LEN+CMD+ΣD) & FF) − 1 | 5A A5 01 FF <cmd> CS | hiworld, wy |
hiworld-aa55 | AA 55 LEN CMD D… CS | sameidem | nothingrien | — |
raise | 2E CMD LEN D… CS | (CMD+LEN+ΣD) XOR FF | byte FFoctet FF | raise, simple, bagoo, binary, oudi, luzheng, keyichuang, xinbas, dogen, hcy, bsj, cyt, autoai, rcw, hechi, xfy, hangdao |
raise-fd / -fd2 | FD LEN CMD D… CS | 8-bit sum (LEN = n+3) / 16-bit (n+4)somme 8 bits (LEN = n+3) / 16 bits (n+4) | nothingrien | — |
Vehicle profiles (librehu-can-vehicle, signal syntax of CliOS):Profils de voiture (librehu-can-vehicle, syntaxe des signaux de CliOS) :
{ "format": "librehu-can-vehicle", "version": 1, "id": "my-car", "box": "hiworld", "baud": 38400,
"link": { "ack": true, "send": [ { "name": "car type", "cmd": "0x24", "data": "00 31", "first_ms": 1000, "every_ms": 30000 } ] },
"variants": [ { "name": "Renault Clio 3", "send": { "car type": "00 31" } } ],
"wheel_keys": { "cmd": "0x11", "code_byte": 2, "state_byte": 3, "pressed": [1], "event_byte": 1, "event_mask": "0x08" },
"messages": { "0x11": { "name": "State", "signals": {
"status": { "start_byte": 0, "bits": { "ACC": 0, "lights": 1 } },
"key": { "start_byte": 2, "mask": "0x1F", "values": { "1": "volume +" } },
"steering": { "start_byte": 6, "size": 2, "sign_magnitude": true, "factor": 0.1, "unit": "°" } } } } }
Signal fields: start_byte (from D0), size 1..8, endian, mask, shift, signed + bit_length, sign_magnitude, factor, offset, min_value / max_value, bits, values, ascii, unit, label. baud: serial speed of the box, set on the MCU at start (0F 00 <index>: 9600 … 460800, 38400 the most common). link: what the head unit sends (ack = acknowledge each message the family's way; send = messages sent first_ms after start then every every_ms, ≥ 500 ms, ≤ 64 bytes; empty data = given by the chosen car, nothing sent before). variants: the cars of one protocol, each one replacing the data of the named messages (its car type). wheel_keys: where the keys are (Raise example: cmd 0x20, code D0, state D1). Built in: renault-clio3-hiworld (LNP002, 94 cars), hyundai-kia-hiworld (HYP001, 152), toyota-hiworld (TYP001, 142), honda-hiworld (HDP004, 79), hiworld-generic (raw); car types replayed from Car Info's buildCarTypePacket. The chosen profile is also written to /sdcard/TWRP/ujc201_can.conf for LibreHU's TWRP. Full reference: docs/can-vehicles.md. CliOS dictionaries index raw CAN ids (0x181…) that never reach the head unit through the box, so they are not importable as is.Champs d'un signal : start_byte (depuis D0), size 1..8, endian, mask, shift, signed + bit_length, sign_magnitude, factor, offset, min_value / max_value, bits, values, ascii, unit, label. baud : vitesse série du boîtier, réglée sur la MCU au départ (0F 00 <index> : 9600 … 460800, 38400 le plus courant). link : ce que l'autoradio envoie (ack = accuser chaque message à la façon de la famille ; send = messages envoyés first_ms après le départ puis toutes les every_ms, ≥ 500 ms, ≤ 64 octets ; data vide = donné par la voiture choisie, rien n'est envoyé avant). variants : les voitures d'un protocole, chacune remplaçant les données des messages nommés (son type de voiture). wheel_keys : où sont les touches (exemple Raise : cmd 0x20, code D0, état D1). Intégrés : renault-clio3-hiworld (LNP002, 94 voitures), hyundai-kia-hiworld (HYP001, 152), toyota-hiworld (TYP001, 142), honda-hiworld (HDP004, 79), hiworld-generic (brut) ; types de voiture rejoués depuis buildCarTypePacket de Car Info. Le profil choisi est aussi écrit dans /sdcard/TWRP/ujc201_can.conf pour le TWRP de LibreHU. Référence complète : docs/can-vehicles.md. Les dictionnaires de CliOS indexent des identifiants CAN bruts (0x181…) qui n'arrivent jamais à l'autoradio par le boîtier : ils ne sont pas importables tels quels.
Service APIAPI du service
AIDL
<queries><package android:name="org.librehu.service" /></queries>
bindService(Intent("org.librehu.service.BIND").setPackage("org.librehu.service"), conn, BIND_AUTO_CREATE)
// onServiceConnected: val api = ILibreHuService.Stub.asInterface(binder)
api.setVolume(20); api.setBalanceFade(30, 20); api.registerCallback(callback)
Copy the .aidl files (package org.librehu.service, plus bt/ and bt/BtParcels.kt for Bluetooth) into the client. Access control works like SuperSU: org.librehu.* and system apps are allowed, any other app gets a SecurityException until the user allows it. Current level: API 8; new methods are only appended, so older clients keep their transaction numbers.Copier les fichiers .aidl (paquet org.librehu.service, plus bt/ et bt/BtParcels.kt pour le Bluetooth) dans le client. Contrôle d'accès façon SuperSU : les applis org.librehu.* et système sont autorisées, toute autre reçoit une SecurityException jusqu'à ce que l'utilisateur l'autorise. Niveau actuel : API 8 ; les nouvelles méthodes ne sont qu'ajoutées à la fin, donc les anciens clients gardent leurs numéros de transaction.
| API | MethodsMéthodes |
|---|---|
| 1 | getApiVersion · getStatus · getMcuVersion · getVehicleFlags · get/setVolume, getMaxVolume · isMuted/setMuted · getTone/setTone(bass, middle, treble) 0..20 · getBalanceFade/setBalanceFade 0..60 (30 = centrecentre) · get/setLoudness 0..15 · isSubwooferOn, getSubwooferLevel, setSubwoofer(on, 0..12) · is/setExternalAmpEnabled · sendMcuFrame(cmd, data) · sendCanData(data) · register/unregisterCallback |
| 2 | setRadioAntenna(on), isRadioAntennaOn (GPIO 110 + MCU 43, cut at ACC off)(GPIO 110 + MCU 43, coupée à la coupure du contact) |
| 3 | getBluetooth(): ILibreHuBluetooth (hands-free, music, phone book)(mains libres, musique, répertoire) |
| 4 | getObdValues(): Bundle (RPM, SPEED, COOLANT_TEMP…, BATTERY)(RPM, SPEED, COOLANT_TEMP…, BATTERY) · getObdState() 0 off, 1 connecting, 2 init, 3 connected, 4 error0 arrêt, 1 connexion, 2 init, 3 connecté, 4 erreur · getMcuProtocol() |
| 5 | resetSoc() (MCU 0E)(MCU 0E) |
| 6 | setAudioSource(0 Android / 1 AUX), getAudioSource |
| 7 | runKeyAction(name) (TouchAction: VOLUME_UP, NEXT, VOICE_ASSISTANT…; routed to Android Auto, EasySource or the sound menu when shown)(TouchAction : VOLUME_UP, NEXT, VOICE_ASSISTANT… ; vers Android Auto, EasySource ou le menu son s'ils sont affichés) |
| 8 | getObdBattery() (voltage, percent, status ENGINE_OFF / CHARGING / NOT_CHARGING / OVERVOLTAGE)(voltage, percent, status ENGINE_OFF / CHARGING / NOT_CHARGING / OVERVOLTAGE) · getObdCarValues() ("<ECU>/<value>" → name, ecu, text, unit, number, error)(« <ECU>/<valeur> » → name, ecu, text, unit, number, error) |
Callback (oneway): onVehicleFlags(flags), onAudioChanged(), onMcuFrame(cmd, data, fromMcu) (both directions), onKey(channel, values, released, learning), onCanData(data).Rappel (oneway) : onVehicleFlags(flags), onAudioChanged(), onMcuFrame(cmd, data, fromMcu) (les deux sens), onKey(channel, values, released, learning), onCanData(data).
| Bit | Flag | MeaningSens |
|---|---|---|
| 0 | FLAG_MCU_ONLINE | MCU link upliaison MCU active |
| 1 | FLAG_ACC | ignition linefil de contact |
| 2 | FLAG_HANDBRAKE | handbrake onfrein à main serré |
| 3 | FLAG_HEADLIGHT | lights onfeux allumés |
| 4 | FLAG_REVERSE | reverse gearmarche arrière |
| 5 / 6 | FLAG_TURN_LEFT / RIGHT | turn signalsclignotants |
| 7 | FLAG_POWERED | powered (ACC with the engine-start delay)alimenté (contact avec le délai de démarrage) |
Broadcasts and intentsDiffusions et intents
| ActionAction | Extras / roleExtras / rôle |
|---|---|
org.librehu.action.VEHICLE_STATE | flags + one boolean per flag (acc, handbrake, headlight, reverse, powered…)flags + un booléen par drapeau (acc, handbrake, headlight, reverse, powered…) |
org.librehu.action.ACC · REVERSE | acc · reverse |
org.librehu.action.OBD | ≈ 1/s, value name → double (RPM, SPEED…), BATTERY_PERCENT, BATTERY_STATUS≈ 1/s, nom de valeur → double (RPM, SPEED…), BATTERY_PERCENT, BATTERY_STATUS |
org.librehu.action.THEME_CHANGED | launcher style, font and palette (same names as the theme provider)style, police et palette du launcher (mêmes noms que le fournisseur de thème) |
org.librehu.action.HOME_VISIBLE | launcher dashboard showntableau de bord du launcher affiché |
org.librehu.action.REQUEST_ROOT / ROOT_RESULT | root handshake (below)demande de root (ci-dessous) |
org.librehu.service.AUDIO_SOURCE · RESET_SOC · SAFE_SHUTDOWN · SAFE_SHUTDOWN_RESUME · REVERSE_VIEW_TOGGLE · SOURCE_SWITCHER · RESTART | commands to the servicecommandes au service |
org.librehu.service.CLAIM_KEYS | BtnRemap takes the key actions (needs HEADUNIT)BtnRemap prend les actions des touches (demande HEADUNIT) |
org.librehu.service.REAR_PREVIEW | Messenger service: the launcher hands a Surface (MSG_ATTACH) and the camera draws straight into itservice Messenger : le launcher confie une Surface (MSG_ATTACH) et la caméra y dessine directement |
Vehicle, ACC, reverse and OBD broadcasts are sent under the signature permission org.librehu.permission.VEHICLE: only apps signed with the LibreHU key receive them.Les diffusions véhicule, contact, marche arrière et OBD partent sous la permission de signature org.librehu.permission.VEHICLE : seules les applis signées avec la clé LibreHU les reçoivent.
Providers and permissionsFournisseurs et permissions
| NameNom | KindType | RoleRôle |
|---|---|---|
org.librehu.permission.VEHICLE | signature | receive vehicle / OBD broadcastsrecevoir les diffusions véhicule / OBD |
org.librehu.permission.HEADUNIT | signature | claim the keys (BtnRemap)réclamer les touches (BtnRemap) |
org.librehu.permission.ROOT_REQUEST | signature | send REQUEST_ROOT; declared by every app so it exists in any install orderenvoyer REQUEST_ROOT ; déclarée par chaque appli pour exister dans tout ordre d'installation |
org.librehu.permission.BRIDGE | signature | call <pkg>.bridge providers and bridgepairappeler les fournisseurs <pkg>.bridge et bridgepair |
org.librehu.permission.SOS | signature | open the launcher's emergency call screenouvrir l'écran d'appel d'urgence du launcher |
content://org.librehu.service.vehicle | provider | vehicle sheet and Bluetooth / Wi-Fi addresses for LibreHU appsfiche du véhicule et adresses Bluetooth / Wi-Fi pour les applis LibreHU |
content://org.librehu.service.bridgepair | provider | call(): pair, cancel, answer, enable, statecall() : pair, cancel, answer, enable, state |
content://<pkg>.bridge | provider | call("pages"), call("set", "page/item", {value}), call("action", …)call("pages"), call("set", "page/élément", {value}), call("action", …) |
content://org.librehu.launcher.theme/theme | provider | style, font and palettestyle, police et palette |
Android 9 does not enforce a provider's read / write permission on call() (ContentProvider.Transport.call): every LibreHU provider checks checkCallingPermission("org.librehu.permission.BRIDGE") itself, through the shared BridgeKit.kt.Android 9 n'applique pas la permission de lecture / écriture d'un fournisseur à call() (ContentProvider.Transport.call) : chaque fournisseur LibreHU vérifie lui-même checkCallingPermission("org.librehu.permission.BRIDGE"), par le BridgeKit.kt commun.
Theme contractContrat de thème
The launcher publishes its look so every app follows it: columns style, font, background, surface, surface_high, on_accent, text, text_dim in content://org.librehu.launcher.theme/theme, same names as extras of org.librehu.action.THEME_CHANGED. style = modern, retro, factory or ambient (factory: black, Source Sans 3; ambient: black to night blue lit by the accent; apps that do not know them treat them as modern). Retro style = car stereo display palette derived from the accent colour (day / night); segment font = "LibreHU Seg14" (from DSEG14 © keshikan, SIL OFL 1.1), upper case without accents.Le launcher publie son apparence pour que toutes les applis la suivent : colonnes style, font, background, surface, surface_high, on_accent, text, text_dim dans content://org.librehu.launcher.theme/theme, mêmes noms que les extras de org.librehu.action.THEME_CHANGED. style = modern, retro, factory ou ambient (usine : noir, Source Sans 3 ; ambiance : du noir au bleu nuit éclairé par l'accent ; les applis qui ne les connaissent pas les traitent comme moderne). Style rétro = palette d'afficheur d'autoradio tirée de la couleur d'accent (jour / nuit) ; police à segments = « LibreHU Seg14 » (tirée de DSEG14 © keshikan, SIL OFL 1.1), en majuscules sans accents.
Widgets: Android 9 refuses app fonts in the restricted context of a home screen (TextView / RemoteViews). SegmentViews inflates the widget layout, applies the segment font, measures it at the widget's real size (OPTION_APPWIDGET_MAX_WIDTH / MIN_HEIGHT, given by the launcher with updateAppWidgetSize), draws each TextView into a bitmap (ellipsis kept) and shows it in an ImageView <id>_seg added next to each text by tools/segment_widgets.py.Widgets : Android 9 refuse les polices d'appli dans le contexte restreint d'un écran d'accueil (TextView / RemoteViews). SegmentViews gonfle le layout du widget, applique la police à segments, le mesure à la vraie taille du widget (OPTION_APPWIDGET_MAX_WIDTH / MIN_HEIGHT, donnée par le launcher avec updateAppWidgetSize), dessine chaque TextView dans une image (ellipse gardée) et l'affiche dans une ImageView <id>_seg ajoutée à côté de chaque texte par tools/segment_widgets.py.
Root handshakeDemande de root
To get every root prompt at once, the launcher sends org.librehu.action.REQUEST_ROOT to each LibreHU package in turn (protected by ROOT_REQUEST); its RootRequestReceiver runs su -c id once and answers org.librehu.action.ROOT_RESULT (package, granted) to reply_to. Nothing but id runs; a second request within 20 s is ignored. Root commands run with a real timeout (output read on its own thread).Pour avoir toutes les demandes de root d'un coup, le launcher envoie org.librehu.action.REQUEST_ROOT à chaque paquet LibreHU à tour de rôle (protégé par ROOT_REQUEST) ; son RootRequestReceiver lance une fois su -c id et répond org.librehu.action.ROOT_RESULT (package, granted) à reply_to. Rien d'autre que id n'est lancé ; une seconde demande dans les 20 s est ignorée. Les commandes root ont un vrai délai maximal (sortie lue sur son propre fil).
Bridge protocol (phone ↔ head unit)Protocole Bridge (téléphone ↔ autoradio)
- Transport: TCP
47800(Wi-Fi) or RFCOMM UUID6c1b2e6a-8f0e-4b8c-9a51-1ebd2d4c0b01; at most 4 connections; a connection that has not finished key exchange and pairing in 80 s is closed.Transport : TCP47800(Wi-Fi) ou RFCOMM UUID6c1b2e6a-8f0e-4b8c-9a51-1ebd2d4c0b01; 4 connexions au plus ; une connexion qui n'a pas fini l'échange de clés et l'association en 80 s est fermée. - Handshake: ephemeral ECDH P-256 both ways; the head unit signs the SHA-256 of the exchange with its permanent ECDSA P-256 key; the phone checks it against the fingerprint from the QR code.Poignée de main : ECDH P-256 éphémère des deux côtés ; l'autoradio signe le SHA-256 de l'échange avec sa clé ECDSA P-256 permanente ; le téléphone la vérifie avec l'empreinte du QR code.
- Session: HKDF-SHA256 (RFC 5869) keys, one per direction; AES-256-GCM frames, nonce = direction + counter (replayed, changed or reordered frames are refused).Session : clés HKDF-SHA256 (RFC 5869), une par sens ; trames AES-256-GCM, nonce = sens + compteur (trame rejouée, modifiée ou déplacée refusée).
- Phone authentication: first time, the QR token (24 random bytes, 5 min, single use) proven by HMAC-SHA256 of the transcript; the head unit then gives an id and a 32-byte access key, proven the same way at each connection (kept in the Android Keystore / iOS keychain). Or Bluetooth-paired phone + confirmation on the head unit (60 s).Authentification du téléphone : la première fois, le jeton du QR code (24 octets aléatoires, 5 min, usage unique) prouvé par HMAC-SHA256 de l'échange ; l'autoradio donne alors un identifiant et une clé d'accès de 32 octets, prouvée de la même façon à chaque connexion (gardée dans le Keystore Android / trousseau iOS). Ou téléphone appairé en Bluetooth + confirmation sur l'autoradio (60 s).
librehu://pair?v=1&fp=<fingerprint>&t=<token>&n=<name>&ip=<IPv4,…>&p=47800&bt=<bluetooth address>
→ {"id":1,"m":"page","p":{"id":"audio"}} ← {"id":1,"r":{…}} | {"id":1,"e":"error"}
← {"ev":"values","d":{…}} (1/s after "subscribe")
| LevelNiveau | MethodsMéthodes |
|---|---|
| 1 | hello · pages · page(id) · set(page, item, value) · action(page, item) · values · subscribe · key(action) · forget |
| 2 | apps (PNG 96 px base64) · launch(pkg) · reboot(normal|recovery|bootloader) · system · gps (satellites, C/N0, fix; never the position)(satellites, C/N0, fix ; jamais la position) · gps.watch(on) · fs.roots · fs.list · fs.read(path, offset, length ≤ 512 KiB) · fs.write · fs.delete · fs.mkdir · fs.rename · logs(logcat|kernel|librehu) · settings.list/put/delete(ns, key) |
Page items: section, info, switch, slider (min, max, step, unit, display), choice (options: {v, l}), text (max), button (confirm, hint). The head unit describes the pages, so a new setting needs no phone update; the phone reloads the page after each change. Files: internal storage and removable volumes only, absolute paths, .. refused, links resolved before checks; the whole file system only with a switch that can only be set on the head unit. Full spec: docs/bridge.md.Éléments de page : section, info, switch, slider (min, max, step, unit, display), choice (options : {v, l}), text (max), button (confirm, hint). L'autoradio décrit les pages, donc un nouveau réglage ne demande pas de mise à jour du téléphone ; le téléphone relit la page après chaque changement. Fichiers : stockage interne et volumes amovibles seulement, chemins absolus, .. refusé, liens résolus avant contrôle ; tout le système de fichiers seulement avec un interrupteur réglable uniquement sur l'autoradio. Spécification complète : docs/bridge.md.
OBD / ddt4all
- ELM327 init:
ATZ ATE0 ATL0 ATS0 ATH0 ATAT1 ATSP0, then0100 0120 0140for supported PIDs; fast values every loop, slow ones every 5 loops;ATRVfor the battery voltage. One session at a time (a stale session stops itself).Init ELM327 :ATZ ATE0 ATL0 ATS0 ATH0 ATAT1 ATSP0, puis0100 0120 0140pour les PID gérés ; valeurs rapides à chaque tour, lentes tous les 5 tours ;ATRVpour la tension de la batterie. Une seule session à la fois (une session périmée s'arrête d'elle-même). - Manufacturer values: ddt4all ECU JSON files (yours, nothing embedded). Addressing like ddt4all:
ATSH <send>,ATFCSH,ATFCSD300000,ATFCSM1,ATCRA <recv>, then back toATSH7DF. Values:firstbytefrom 1 (service byte included),bitoffsetfrom the MSB,scaled = (raw × step + offset) / divideby; checked against ddt4all's Python on the same bytes (ObdCarTest).Valeurs constructeur : fichiers JSON de calculateurs ddt4all (les vôtres, rien d'embarqué). Adressage comme ddt4all :ATSH <envoi>,ATFCSH,ATFCSD300000,ATFCSM1,ATCRA <réception>, puis retour àATSH7DF. Valeurs :firstbytedepuis 1 (octet de service compris),bitoffsetdepuis le bit de poids fort,scaled = (brut × step + offset) / divideby; comparé au Python de ddt4all sur les mêmes octets (ObdCarTest). - Read-only by design: only services
21/22without parameters are kept at import; no write, reset, actuator test or diagnostic session is ever sent; adapter commands that write its memory (ATPP,ATSD…) are refused.Lecture seule par construction : seuls les services21/22sans paramètre sont gardés à l'import ; aucune écriture, aucun reset, aucun test d'actionneur ni ouverture de session de diagnostic n'est jamais envoyé ; les commandes qui écrivent la mémoire de l'adaptateur (ATPP,ATSD…) sont refusées.
Build, CI, releasesBuild, CI, releases
| TopicSujet | RuleRègle |
|---|---|
| EditionsÉditions | launcher, FM, Dialer, Widgets, BtnRemap: commit on main, merge into ivi and librehu-service. Forks: branch librehu (+ master / main published). ViPER: librehu-service.launcher, FM, Dialer, Widgets, BtnRemap : commit sur main, fusion dans ivi et librehu-service. Forks : branche librehu (+ master / main publiées). ViPER : librehu-service. |
| Releases | each push publishes a rolling prerelease latest-<branch> (APK + release.txt: versionCode, versionName, commit, name, size, SHA-256) and a frozen build-<branch>-<versionCode>chaque push publie une pré-release continue latest-<branche> (APK + release.txt : versionCode, versionName, commit, nom, taille, SHA-256) et une figée build-<branche>-<versionCode> |
| Versions | versionCode = number of commits of the branch; versionName = git describe (rolling tags excluded) + -<branch>. Rewriting history must keep the commit count, or Android refuses the next update as a downgrade.versionCode = nombre de commits de la branche ; versionName = git describe (tags continus exclus) + -<branche>. Réécrire l'historique doit garder le nombre de commits, sinon Android refuse la mise à jour suivante comme une rétrogradation. |
| SigningSignature | keystore/librehu.jks, alias and passwords librehu, public on purpose (like Android's debug key). RSA 2048, SHA384withRSA, CN=LibreHU. Certificate SHA-256 91:A1:E7:A1:CA:2C:51:DA:CB:FD:63:8A:15:07:18:7B:40:D9:40:03:94:D2:90:2A:32:15:F6:10:D3:FC:00:DC. A private key can replace it with the CI secret LIBREHU_KEYSTORE_B64.keystore/librehu.jks, alias et mots de passe librehu, public exprès (comme la clé debug d'Android). RSA 2048, SHA384withRSA, CN=LibreHU. Certificat SHA-256 91:A1:E7:A1:CA:2C:51:DA:CB:FD:63:8A:15:07:18:7B:40:D9:40:03:94:D2:90:2A:32:15:F6:10:D3:FC:00:DC. Une clé privée peut la remplacer par le secret CI LIBREHU_KEYSTORE_B64. |
| R8 | isMinifyEnabled = true, -dontobfuscate (readable stack traces); usage.txt in the CI's "R8 reports" artifact shows what was removed.isMinifyEnabled = true, -dontobfuscate (traces lisibles) ; usage.txt dans l'artefact CI « R8 reports » montre ce qui a été retiré. |
| OTA catalogueCatalogue OTA | catalog.json of LibreHU-OTA-app: apps (id, name, repo, package, branches, tagged, asset regex, cert SHA-256 for non-LibreHU APKs) and configs (type FILE / RECOVERY_ZIP, url, dest, mode, owner, sha256, reboot). tools/check_releases.py checks every published branch.catalog.json de LibreHU-OTA-app : applis (id, name, repo, package, branches, tagged, regex asset, cert SHA-256 pour les APK hors LibreHU) et configs (type FILE / RECOVERY_ZIP, url, dest, mode, owner, sha256, reboot). tools/check_releases.py vérifie chaque branche publiée. |
| Zips | release zips of LibreHU-flashablesZips, rebuilt hourly: index.json with categories and zips (id, category, devices, version, url, size, sha256, image_url, image_sha256, partition, restore, warning, license)release zips de LibreHU-flashablesZips, refaite toutes les heures : index.json avec categories et zips (id, category, devices, version, url, size, sha256, image_url, image_sha256, partition, restore, warning, license) |
# any appn'importe quelle appli (JDK 17+, Android SDK; NDK for service, FM, Disc)
./gradlew :app:assembleRelease
# service core only, no Android SDKcœur du service seul, sans SDK Android
./gradlew -PcoreOnly :core:test
# iPhone appappli iPhone (Xcode 16, XcodeGen)
cd ios && xcodegen generate && xcodebuild -project LibreHU.xcodeproj -scheme LibreHU -sdk iphoneos CODE_SIGNING_ALLOWED=NO build
Custom kernelNoyau modifié
The LibreHU kernel starts from the GPL source package Jancar's distributor sent on 19/10/2026 (Autochips reference kernel, Linux 4.9.117), with what the package lacked rebuilt from the stock image. It boots the stock ROM from boot, at parity with the stock kernel for Wi-Fi, BT, GPS, FM, audio, touch, MCU, rear camera and CVBS inputs.Le noyau LibreHU part du paquet de sources GPL envoyé par le distributeur de Jancar le 19/10/2026 (noyau de référence Autochips, Linux 4.9.117), avec ce qui manquait au paquet reconstruit depuis l'image d'origine. Il démarre la ROM d'origine depuis boot, à parité avec le noyau d'origine pour le Wi-Fi, le BT, le GPS, la FM, l'audio, le tactile, la MCU, la caméra de recul et les entrées CVBS.
| BranchBranche | ContentContenu |
|---|---|
autochips-gpl | the GPL package as received (kernel-4.9.tgz, SHA-256 f0303951…7d83c577), untouchedle paquet GPL tel que reçu (kernel-4.9.tgz, SHA-256 f0303951…7d83c577), intact |
reverse-engineering | earlier kernel rebuilt from MediaTek MT6761 sources + reverse engineeringnoyau reconstruit plus tôt depuis les sources MediaTek MT6761 + rétro-ingénierie |
LibreHU-main | autochips-gpl + LibreHU changes (main branch until 01/11/2026, no longer worked on nor rebuilt since then; its last build stays in the OTA); defconfig ac8257_ujc201_defconfig = stock #72 configautochips-gpl + modifications LibreHU (branche principale jusqu'au 01/11/2026, plus de travail ni de compilation depuis ; son dernier build reste dans l'OTA) ; defconfig ac8257_ujc201_defconfig = config de l'origine #72 |
connectivity-builtin | Wi-Fi / BT / GPS / FM GPL drivers built in instead of vendor modules (no longer rebuilt since 01/11/2026; its last build stays in the OTA)pilotes GPL Wi-Fi / BT / GPS / FM intégrés au lieu des modules vendor (plus recompilée depuis le 01/11/2026 ; son dernier build reste dans l'OTA) |
stable-4.9.337 | official linux-stable fixes from 4.9.118 to 4.9.337 (last 4.9 release), applied in 7 stacked batches, this branch being the last one; the stable kernel, used daily on the unit without problems (04/11/2026); the working branch since 01/11/2026 and the only one the CI builds (fixes, CI at each push, OTA zips); validated on the unit (USB, MCU, Bluetooth, Wi-Fi, hotspot, root, FM, video inputs, camera, standby); method, decisions per commit, audit and tests: docs/STABLE.mdcorrectifs officiels linux-stable de 4.9.118 à 4.9.337 (dernière version 4.9), appliqués en 7 lots empilés, cette branche étant le dernier ; le noyau stable, utilisé tous les jours sur l'autoradio sans problème (04/11/2026) ; branche de travail depuis le 01/11/2026 et seule compilée par la CI (correctifs, CI à chaque push, zips de l'OTA) ; validée sur l'autoradio (USB, MCU, Bluetooth, Wi-Fi, point d'accès, root, FM, entrées vidéo, caméra, veille) ; méthode, décisions commit par commit, audit et tests : docs/STABLE.md |
legacy-4.9.* | batches 1 to 6 (legacy-4.9.150, -200, -225, -250, -300, -319: the stable-4.9.<N> branches renamed on 01/11/2026), with the LibreHU fixes up to r134; not maintained and not in the OTA, only kept to find which linux-stable batch broke something; no longer rebuilt (last builds kept as prereleases, runs 135 to 140): docs/STABLE.mdlots 1 à 6 (legacy-4.9.150, -200, -225, -250, -300, -319 : les branches stable-4.9.<N> renommées le 01/11/2026), avec les correctifs LibreHU jusqu'à r134 ; non maintenues et hors OTA, gardées seulement pour retrouver le lot linux-stable qui a cassé quelque chose ; plus recompilées (derniers builds gardés en prereleases, runs 135 à 140) : docs/STABLE.md |
- Root: KernelSU Next v3.4.2-legacy (API 5, for the v3.4.2 manager) with manual hooks (manager hash from
KSU_NEXT_MANAGER_HASH). Fixed on 27/10: the upstream 4.9 build fix (165b87fa) disabled the SELinux AVC cache once KernelSU applied its rules, making boot take hours on the kernels published on 26/10; the fix (reset_avc_cachewith seqno 0) is in every branch.Root : KernelSU Next v3.4.2-legacy (API 5, pour le gestionnaire v3.4.2) avec hooks manuels (empreinte du gestionnaire dansKSU_NEXT_MANAGER_HASH). Corrigé le 27/10 : le correctif amont pour 4.9 (165b87fa) désactivait le cache AVC de SELinux dès que KernelSU appliquait ses règles, et le démarrage prenait des heures avec les noyaux publiés le 26/10 ; la correction (reset_avc_cacheavec seqno 0) est dans toutes les branches. - CPU: 16 OPPs up to 2.6 GHz, 2.3 GHz at boot (2.4 GHz stable under Geekbench 5); voltages rising above 2.0 GHz up to 1.05 V at 2.2 GHz, then 1.05 V up to 2.6 GHz since
stable-4.9.337r129 (30/10: the SSPM keeps Vsram, the SRAM of the cores, at Vproc + 0.1 V capped at 1.05 V, measured on the unit; the 1.0625–1.11875 V of r127 at 2.3–2.6 GHz ran with Vsram below Vproc, the 2.5 / 2.6 GHz hangs; the 2.4 GHz Geekbench run was at 1.050 V); real Vproc (PMIC register, root):cat /proc/cpufreq/MT_CPU_DVFS_LL/cpufreq_volt, command in docs/TUNING.md; adjustableac8257_cpufreq.volt_uv/volt_offset_uv(0.65–1.05 V, sent to the SSPM; also from the command line since r129). GPU 730 MHz and adjustable VCORE (ac8257_gpufreq.vcore_uv, 0.60–0.90 V). Governors conservative and schedutil added (interactive stays the default), zram lz4, deadline I/O, touch boost (effective sincestable-4.9.337r128, 30/10: until then it went through the PPM system boost, which the vendor init leaves disabled, and did nothing). Frequency limits respected by every governor sincestable-4.9.337r133 (31/10, same code as r131): the CPU could stay at its maximum whatever the governor (the SSPM bounds were copied intoscaling_min/max_freqand frozen by a governor change, requests were bounded twice, a request equal to the current frequency was dropped; upstreamCPUFREQ_NEED_UPDATE_LIMITSbackported), the SSPM starts within the boot maximum, vendor OPP indexes are read in the stock table, and a GPU kept at one OPP is throttled by the thermal limit again; r134 (01/11): the MediaTek USB boost, which held the CPU at 2.5 GHz at least and the DRAM at its maximum while a PC used MTP (the "always at the maximum" seen on the unit), is off by default and tunable, from SmartPack-Kernel Manager custom controls for instance (/sys/class/usb_boost/cpu_freq/enable,arg1,timeout). Details: docs/TUNING.md.CPU : 16 OPP jusqu'à 2,6 GHz, 2,3 GHz au démarrage (2,4 GHz stable sous Geekbench 5) ; tensions croissantes au-dessus de 2,0 GHz jusqu'à 1,05 V à 2,2 GHz, puis 1,05 V jusqu'à 2,6 GHz depuisstable-4.9.337r129 (30/10 : la SSPM garde Vsram, la SRAM des cœurs, à Vproc + 0,1 V plafonnée à 1,05 V, mesuré sur l'autoradio ; les 1,0625 à 1,11875 V de r127 à 2,3–2,6 GHz tournaient avec Vsram sous Vproc, d'où les plantages à 2,5 / 2,6 GHz ; le Geekbench à 2,4 GHz tournait à 1,050 V) ; Vproc réelle (registre du PMIC, root) :cat /proc/cpufreq/MT_CPU_DVFS_LL/cpufreq_volt, commande dans docs/TUNING.md ; réglables parac8257_cpufreq.volt_uv/volt_offset_uv(0,65–1,05 V, envoyées à la SSPM ; aussi par la ligne de commande depuis r129). GPU 730 MHz et VCORE réglable (ac8257_gpufreq.vcore_uv, 0,60–0,90 V). Gouverneurs conservative et schedutil ajoutés (interactive reste par défaut), zram lz4, E/S deadline, boost tactile (effectif depuisstable-4.9.337r128, 30/10 : il passait jusque-là par le boost système de la PPM, que l'init vendor laisse désactivé, et ne faisait rien). Limites de fréquence respectées par tous les gouverneurs depuisstable-4.9.337r133 (31/10, même code que r131) : le CPU pouvait rester au maximum quel que soit le gouverneur (bornes de la SSPM recopiées dansscaling_min/max_freqet figées par un changement de gouverneur, requêtes bornées deux fois, requête égale à la fréquence courante abandonnée ;CPUFREQ_NEED_UPDATE_LIMITSde l'amont rétroporté), la SSPM démarre sous le maximum de démarrage, les index d'OPP du vendor sont lus dans la table d'origine, et un GPU fixé à un OPP est de nouveau bridé par le thermique ; r134 (01/11) : le boost USB de MediaTek, qui gardait le CPU à 2,5 GHz au moins et la DRAM au maximum tant qu'un PC utilisait le MTP (le « toujours au maximum » vu sur l'autoradio), est coupé par défaut et réglable, par exemple depuis les Custom Controls de SmartPack-Kernel Manager (/sys/class/usb_boost/cpu_freq/enable,arg1,timeout). Détail : docs/TUNING.md. - Thermal policy,
stable-4.9.337r129: the kernel replaces what the vendorthermal_managerwrites at boot. CPU: reset 125 °C, brakes 105 °C, adaptive regulation (ATM) aiming at 100 °C with the CTM off (the vendor aimed at 55 °C as soon as the board passed 46 °C, which held the overclock back). Board sensor (AP thermistor): Android shut down at 105 °C, reset at 107 °C (the vendor enabled none).ac8257_thermal.policy=0on the command line gives back the vendor values. Details: docs/TUNING.md.Politique thermique,stable-4.9.337r129 : le noyau remplace ce quethermal_manager(vendor) écrit au démarrage. CPU : reset 125 °C, freins 105 °C, régulation adaptative (ATM) visant 100 °C avec le CTM coupé (le vendor visait 55 °C dès que la carte dépassait 46 °C, ce qui bridait l'overclock). Capteur de la carte (thermistance AP) : arrêt d'Android à 105 °C, reset à 107 °C (le vendor n'en activait aucun).ac8257_thermal.policy=0sur la ligne de commande redonne les valeurs du vendor. Détail : docs/TUNING.md. - Rebuilt drivers: Jancar
/dev/gpios_ioctl(CONFIG_JANCAR_GPIOS), TP9963 AHD camera decoder (tp9963_yuv, from the stock image, cross-checked with OpenIPC and Allwinner GPL drivers), Jancar touch calibration nodes, IR remote table (rc-jancar), other touch panels (FocalTech FT8201, ILITEK, Himax HX83102, Hynitron CST340).Pilotes reconstruits :/dev/gpios_ioctlde Jancar (CONFIG_JANCAR_GPIOS), décodeur caméra AHD TP9963 (tp9963_yuv, depuis l'image d'origine, recoupé avec les pilotes GPL OpenIPC et Allwinner), nœuds de calibration tactile Jancar, table de télécommande IR (rc-jancar), autres dalles (FocalTech FT8201, ILITEK, Himax HX83102, Hynitron CST340). - Added: USB CD / DVD (
BLK_DEV_SR,CHR_DEV_SG,UDF_FS, plus a fix of a 4.9 NULLrq->sensepanic incdrom_read_cdda_bpc), USB Ethernet (RTL8152/8153, RNDIS, iPhone, LAN78xx, SMSC…), boot menu image (TWRP / Android / Fastboot).Ajouts : CD / DVD USB (BLK_DEV_SR,CHR_DEV_SG,UDF_FS, plus la correction d'une panique 4.9 surrq->senseNULL danscdrom_read_cdda_bpc), Ethernet USB (RTL8152/8153, RNDIS, iPhone, LAN78xx, SMSC…), image avec menu de démarrage (TWRP / Android / Fastboot). - Standby (suspend / resume),
stable-4.9.337: CPU offline no longer hangs (29/10, every stable branch: since stable 4.9.120 the MediaTeksmpboot_park_threads()loop waited forcpuhp/N, the thread running it); first real standby on 29/10 (52.9 s, woken by the MCU's power key pulse); r126 runs again on resume the panel init kept in Jancar's MetaZone (MIPI DSI table, missing from the GPL package), the panel stayed black; r127 (30/10): panel power-off cancelled on a quick screen off / on, display composer thread asleep while the screen is off (no more 10 "wait VSYNC timeout" a second), GT9xx touch woken with one reset instead of five, a USB enumeration abort during suspend no longer leavesaddress0_mutexheld (stable 4.9.292 interplay), camera regulator leak; r130 (31/10): when userspace does not report the end of the resume within 10 s the kernel does it (the USB stack stayed in the AutoChips suspend mode), the ports whose enumeration the suspend cut short are enumerated again, andandroid_setup()of the Android USB gadget is locked against a USB function change (adb, MTP, CarPlay). 01/11: r143 reads the panel init table at its real MetaZone indexes (r126 read it out of range, so the panel was powered but never initialised): the panel comes back after the standby (validated on the unit), and USB with the CD drive works after waking; no sound after waking: LibreHU-service did not send the BD37534 its power-on sequence and input again after the power cut, it now does at the screen on that follows a suspend (01/11, to test); adb, back only after a host / device switch: r144 reconnects the USB OTG port in its role at the end of the resume (to test). Details: docs/STABLE.md.Veille (suspension / reprise),stable-4.9.337: l'extinction des cœurs ne bloque plus (29/10, toutes les branches stable : depuis la stable 4.9.120 la boucle MediaTek desmpboot_park_threads()attendaitcpuhp/N, le thread qui l'exécute) ; première vraie veille le 29/10 (52,9 s, réveil par l'impulsion « power » de la MCU) ; r126 relance au réveil l'init de la dalle gardée dans la MetaZone de Jancar (table MIPI DSI, absente du paquet GPL), la dalle restait noire ; r127 (30/10) : coupure de la dalle annulée sur un écran éteint puis rallumé vite, thread de composition qui dort écran éteint (plus de 10 « wait VSYNC timeout » par seconde), tactile GT9xx réveillé en un reset au lieu de cinq, un abandon d'énumération USB pendant la veille ne laisse plusaddress0_mutexverrouillé (effet de la stable 4.9.292), fuite de régulateur caméra ; r130 (31/10) : si l'espace utilisateur ne signale pas la fin de la reprise dans les 10 s, le noyau le fait (l'USB restait dans le mode « veille » d'AutoChips), les ports dont l'énumération a été écourtée par la veille sont ré-énumérés, etandroid_setup()du gadget USB Android est verrouillé contre un changement de fonctions USB (adb, MTP, CarPlay). 01/11 : r143 lit la table d'init de la dalle à ses vrais index de la MetaZone (r126 la lisait hors limites : la dalle était alimentée mais jamais initialisée) : la dalle revient après la veille (validé sur l'autoradio), et l'USB avec le lecteur CD marche au réveil ; pas de son au réveil : LibreHU-service ne renvoyait pas au BD37534 sa séquence de démarrage ni l'entrée après la coupure, il le fait maintenant à l'allumage d'écran qui suit une suspension (01/11, à tester) ; adb, qui ne revenait qu'après une bascule hôte / périphérique : r144 rebranche le port USB OTG dans son rôle à la fin de la reprise (à tester). Détail : docs/STABLE.md. - Fixes vs the package: no TDA7729 ALSA controls (absent chip, panic at 6.5 s), camera decoder power like stock (panic at 26 s), no
mmc0/sw_wp_en(made/systemread-only), no X axis inversion on the GT928 driver.Corrections par rapport au paquet : pas de contrôles ALSA TDA7729 (puce absente, panique à 6,5 s), alimentation des décodeurs caméra comme l'origine (panique à 26 s), pas demmc0/sw_wp_en(rendait/systemnon inscriptible), pas d'inversion de l'axe X du pilote GT928.
# toolchain of the stock kernel: AOSP GCC 4.9 binutils + AOSP clang 6.0.2 (clang-4691093), Python 2 for DrvGen.pychaîne du noyau d'origine : binutils AOSP GCC 4.9 + AOSP clang 6.0.2 (clang-4691093), Python 2 pour DrvGen.py
git clone -b LibreHU-main https://github.com/LibreHU/android_kernel_autochips_ac8257_4.9 kernel && cd kernel
git clone --depth 1 -b lineage-17.1 https://github.com/LineageOS/android_prebuilts_gcc_linux-x86_aarch64_aarch64-linux-android-4.9 ../gcc49
mkdir -p ../clang && curl -fL https://android.googlesource.com/platform/prebuilts/clang/host/linux-x86/+archive/refs/tags/android-9.0.0_r61/clang-4691093.tar.gz | tar xz -C ../clang
export PATH="$HOME/ac8257-host:$PWD/../clang/bin:$PWD/../gcc49/bin:$PATH" LD_LIBRARY_PATH="$HOME/ac8257-host"
export KBUILD_BUILD_USER=dokt KBUILD_BUILD_HOST=librehu # "Linux version ... (dokt@librehu)", like the CI« Linux version ... (dokt@librehu) », comme la CI
M='O=out ARCH=arm64 CROSS_COMPILE=aarch64-linux-android- CC=clang CLANG_TRIPLE=aarch64-linux-gnu-'
M="$M ATC_DISABLE_BATTERY_CHARGER=yes ATC_FASTDISP_VERSION=2.0 python=python"
make $M HOSTCC="gcc -fcommon" ac8257_ujc201_defconfig
make $M HOSTCC="gcc -fcommon" -j"$(nproc)" Image.gz-dtb
# images from the stock templatesimages depuis les gabarits d'origine
python3 tools/ac8257/repack_boot.py tools/ac8257/stock/boot_template_250718.img out/arch/arm64/boot/Image.gz-dtb \
boot_ac8257.img --size 10485760
~/ac8257-host holds Python 2.7 and libtinfo.so.5 (Ubuntu 24.04 / 26.04 lack them). Exact steps, also for a recovery test image: docs/BUILD.md; status per function: README; overclock: docs/TUNING.md.~/ac8257-host contient Python 2.7 et libtinfo.so.5 (absents d'Ubuntu 24.04 / 26.04). Étapes exactes, aussi pour une image de test en recovery : docs/BUILD.md ; état par fonction : README ; overclock : docs/TUNING.md.
TWRP
android_device_alps_ac8257_demo, branch twrp (TWRP 12.1). The image is post-processed: stock kernel with skip_initramfs → want_initramfs, stock dtbo, and the stock recovery's vbmeta as AVB footer (BOARD_AVB_ENABLE := false). touchfix (static aarch64, no libc) fixes touch, front keys and inverted brightness; the BCB is redirected to para; MCU state in the status bar; key navigation from the steering wheel and the Hiworld CAN box (channel 10 frames). CI releases twrp-build-<n>.android_device_alps_ac8257_demo, branche twrp (TWRP 12.1). L'image est post-traitée : noyau d'origine avec skip_initramfs → want_initramfs, dtbo d'origine, et le vbmeta du recovery d'origine en pied AVB (BOARD_AVB_ENABLE := false). touchfix (aarch64 statique, sans libc) corrige le tactile, les touches de façade et la luminosité inversée ; le BCB est redirigé vers para ; état de la MCU dans la barre d'état ; navigation au volant et par le boîtier CAN Hiworld (trames 10). Releases CI twrp-build-<n>.
LineageOS in preparationen préparation
A real ROM, not a GSI: LineageOS 16.0 (Android 9) for the UJC201 on branch lineage-16.0 of the device tree: LineageOS system + stock vendor (extracted with extract-files.sh from your own unit, never published) + the LibreHU apps in /system/app, precompiled with the speed filter, ViPER4Android and its ViPERFX_RE driver included. Kernel (01/11/2026): the LibreHU kernel stable-4.9.337 built by its CI (tools/fetch_kernel.sh, KernelSU Next for root), its Wi-Fi / BT / GPS / FM modules put in your vendor image (tools/vendor_add_conn_modules.sh), the stock kernel otherwise. More LineageOS versions are planned once it runs.Une vraie ROM, pas une GSI : LineageOS 16.0 (Android 9) pour l'UJC201 sur la branche lineage-16.0 de l'arbre de l'appareil : système LineageOS + vendor d'origine (extrait avec extract-files.sh de votre propre autoradio, jamais publié) + les applis LibreHU dans /system/app, précompilées avec le filtre speed, ViPER4Android et son pilote ViPERFX_RE compris. Noyau (01/11/2026) : le noyau LibreHU stable-4.9.337 compilé par sa CI (tools/fetch_kernel.sh, KernelSU Next pour le root), ses modules Wi-Fi / BT / GPS / FM mis dans votre image vendor (tools/vendor_add_conn_modules.sh), le noyau d'origine sinon. D'autres versions de LineageOS sont prévues une fois celle-ci fonctionnelle.
Porting to other platformsPortage sur d'autres plateformes
LibreHU targets the UJC201 first; once it is mature, it will be adapted to other head units. What is already data-driven:LibreHU vise d'abord l'UJC201 ; une fois mûr, il sera adapté à d'autres autoradios. Ce qui est déjà piloté par des données :
- MCU protocol: JSON profiles (serial port and speed, framing, checksum, ACK, signals, commands, GPIO wiring, audio chip address).Protocole MCU : profils JSON (port et vitesse, trame, contrôle, acquittement, signaux, commandes, câblage GPIO, adresse de la puce audio).
- Car: CAN vehicle profiles (JSON) and OBD car files; generic OBD-II works on any car.Voiture : profils CAN (JSON) et fichiers voiture OBD ; l'OBD-II générique marche sur toute voiture.
- Apps: the
mainedition runs on any Android 9+ device; LibreHU Helper collects what a port needs.Applis : l'éditionmaintourne sur tout appareil Android 9+ ; LibreHU Helper rassemble ce qu'il faut pour un portage.
Still tied to the UJC201, from easiest to hardest:Encore liés à l'UJC201, du plus simple au plus difficile :
| # | Hard-codedCodé en dur | WhereOù |
|---|---|---|
| 1 | "stock service running" guard watches com.jancar.services onlyla garde « service d'origine actif » ne surveille que com.jancar.services | LibreHuService.startHardware |
| 2 | native code arm64 onlycode natif en arm64 seulement | abiFilters |
| 3 | no inverted-XOR checksum (Raise boxes) in profilespas de contrôle « XOR inversé » (boîtiers Raise) dans les profils | McuProfile.kt |
| 4 | CAN box: Hiworld only, relayed by the MCU (10); CAN steering keys: Hiworld 0x11boîtier CAN : Hiworld seulement, relayé par la MCU (10) ; touches CAN : Hiworld 0x11 | core/can/Hiworld.kt, CanMonitor, CanWheelKeys |
| 5 | GPIO only through /dev/gpios_ioctlGPIO uniquement par /dev/gpios_ioctl | NativeHw |
| 6 | one sound processor (BD37534)un seul processeur de son (BD37534) | McuProfile.kt |
| 7 | Jancar / Autochips extras: touch keys and calibration (/jancar/config), microphone, CVBS / camera, video out, ATCextras Jancar / Autochips : touches tactiles et calibration (/jancar/config), micro, CVBS / caméra, sortie vidéo, ATC | touch/, Microphone, car/, atc/ |
Most common head unit chips and their real Android versions: head units. To help a port: run LibreHU Helper on the target unit and open an issue with its report.Puces d'autoradio les plus courantes et leurs vraies versions d'Android : autoradios. Pour aider un portage : lancer LibreHU Helper sur l'autoradio visé et ouvrir une issue avec son rapport.
Repositories and rulesDépôts et règles
| RepositoryDépôt | Package / contentPaquet / contenu | Branches |
|---|---|---|
| LibreHU-service | org.librehu.service | main |
| LibreHU-Launcher-App | org.librehu.launcher | main, ivi, librehu-service |
| LibreHU-OTA-app | org.librehu.ota | main |
| LibreHU-FM-app · dialer · widget · BtnRemap | org.librehu.fm · .dialer · .widgets · .btnremap | main, ivi, librehu-service |
| EasySource · disc · helper · bridge | org.librehu.easysource · .disc · .helper · phone appappli téléphone | main |
| ViPER4Android | com.llsl.viper4android | librehu-service |
| Open Headunit · xcertplay | org.librehu.headunit · org.librehu.carplay | librehu (+ main / master) |
| android_kernel_autochips_ac8257_4.9 | kernelnoyau | LibreHU-main, autochips-gpl, reverse-engineering, connectivity-builtin, stable-4.9.337, legacy-4.9.* |
| android_device_alps_ac8257_demo | TWRP, LineageOS device treeTWRP, arbre LineageOS | twrp, lineage-16.0 |
| LibreHU-flashablesZips | flashable zips, index.jsonzips flashables, index.json | main |
| MCU-tools-app | com.dokt.jacmcu (legacy), MCU firmware analysiscom.dokt.jacmcu (legacy), analyse du firmware MCU | main |
- Never published: vendor images and proprietary blobs (LineageOS extracts them from your own unit), and any unit's own partitions (nvram, nvdata, proinfo, persist, metazone).Jamais publiés : images vendor et blobs propriétaires (LineageOS les extrait de votre propre autoradio), et les partitions propres à un autoradio (nvram, nvdata, proinfo, persist, metazone).
- Verified boot: nothing disables dm-verity or writes vbmeta automatically.Démarrage vérifié : rien ne désactive dm-verity ni n'écrit vbmeta automatiquement.
- Forks: upstream changes are merged into the
librehubranch, LibreHU code stays markedLibreHU fork. Last merge (27/10/2026): Open Headunit 4.0.0-beta1, xcertplay 1.4.2; ViPER4Android has nothing new. Details in each fork'sREADME_LIBREHU.md.Forks : les nouveautés d'amont sont fusionnées dans la branchelibrehu, le code LibreHU reste marquéLibreHU fork. Dernière fusion (27/10/2026) : Open Headunit 4.0.0-beta1, xcertplay 1.4.2 ; rien de nouveau pour ViPER4Android. Détails dans leREADME_LIBREHU.mdde chaque fork. - Contributing: issues and pull requests on each repository; attach logs or a Helper report.Contribuer : issues et pull requests sur chaque dépôt ; joindre des journaux ou un rapport de Helper.