Update README with all code review changes
This commit is contained in:
parent
a80abc5c0c
commit
4a9a15bca5
1 changed files with 37 additions and 30 deletions
67
README.md
67
README.md
|
|
@ -63,12 +63,12 @@ De sequence counter reset naar 0 bij elke (re)connectie op de ESP. De app start
|
||||||
|
|
||||||
- De HMAC key wordt **niet meegecompileerd** — de ESP start zonder key en wordt geprovisioneerd via USB
|
- De HMAC key wordt **niet meegecompileerd** — de ESP start zonder key en wordt geprovisioneerd via USB
|
||||||
- De key wordt opgeslagen in **NVS** (Non-Volatile Storage) op de ESP via de Arduino `Preferences` library (survives reboots en power-cycles)
|
- De key wordt opgeslagen in **NVS** (Non-Volatile Storage) op de ESP via de Arduino `Preferences` library (survives reboots en power-cycles)
|
||||||
- De app slaat de key op in **SharedPreferences** (overleeft app-herstarts)
|
- De app slaat de key op in **EncryptedSharedPreferences** (AES256-GCM versleuteld, overleeft app-herstarts)
|
||||||
- **USB auto-provisioning:** Sluit de ESP via USB-OTG aan op de telefoon. De app stuurt `KEY?\n`:
|
- **USB provisioning via Instellingen:** Open Instellingen → Sleutel Uitwisselen. Sluit de ESP via USB-OTG aan. De app stuurt `KEY?\n`:
|
||||||
- Als de ESP al een key heeft → ESP antwoordt `HMAC_KEY:<hex>` → app slaat op lokaal op
|
- Als de ESP al een key heeft → ESP antwoordt `HMAC_KEY:<hex>` → app slaat op lokaal op
|
||||||
- Als de ESP nog geen key heeft → ESP antwoordt `NO_KEY` → app genereert willekeurige 32-byte key via `SecureRandom` → stuurt `SET_KEY:<hex>\n` → ESP slaat op in NVS en antwoordt `OK` → app slaat op lokaal op
|
- Als de ESP nog geen key heeft → ESP antwoordt `NO_KEY` → app genereert willekeurige 32-byte key via `SecureRandom` → stuurt `SET_KEY:<hex>\n` → ESP slaat op in NVS en antwoordt `OK` → app slaat op lokaal op
|
||||||
- Beide paden tonen dezelfde toast: **"Beveiligingssleutel gelezen"**
|
- Beide paden tonen dezelfde toast: **"Beveiligingssleutel gelezen"**
|
||||||
- Zo kan dezelfde app gebruikt worden voor meerdere ESP modules — each met een eigen key
|
- Zo kan dezelfde app gebruikt worden voor meerdere ESP modules — elk met een eigen key
|
||||||
|
|
||||||
#### USB Serial Protocol
|
#### USB Serial Protocol
|
||||||
|
|
||||||
|
|
@ -83,12 +83,12 @@ ESP → App: OK\n (bevestiging)
|
||||||
|
|
||||||
De ESP luistert in `loop()` voor USB serial data. Bij `KEY?\n` antwoordt hij met de hex key (`HMAC_KEY:<hex>`) of `NO_KEY`. Bij `SET_KEY:<64-hex>\n` slaat hij de key op in NVS en antwoordt `OK`.
|
De ESP luistert in `loop()` voor USB serial data. Bij `KEY?\n` antwoordt hij met de hex key (`HMAC_KEY:<hex>`) of `NO_KEY`. Bij `SET_KEY:<64-hex>\n` slaat hij de key op in NVS en antwoordt `OK`.
|
||||||
|
|
||||||
#### Provisioning flow (auto)
|
#### Provisioning flow (via Instellingen)
|
||||||
|
|
||||||
```
|
```
|
||||||
Android app ESP32 (USB CDC)
|
Android app ESP32 (USB CDC)
|
||||||
─────────── ───────────────
|
─────────── ───────────────
|
||||||
USB device attached ──────────►
|
User taps "Sleutel Uitwisselen"
|
||||||
Request USB permission ◄────── Android dialog
|
Request USB permission ◄────── Android dialog
|
||||||
Open CDC-ACM connection
|
Open CDC-ACM connection
|
||||||
Send KEY?\n ──────────────────►
|
Send KEY?\n ──────────────────►
|
||||||
|
|
@ -111,7 +111,7 @@ De ESP luistert in `loop()` voor USB serial data. Bij `KEY?\n` antwoordt hij met
|
||||||
| **Commando vervalsing** | HMAC dekt command byte + sequence — wijzigen maakt handtekening ongeldig |
|
| **Commando vervalsing** | HMAC dekt command byte + sequence — wijzigen maakt handtekening ongeldig |
|
||||||
| **Brute-force key** | 32-byte key = 2^256 mogelijkheden — onhaalbaar |
|
| **Brute-force key** | 32-byte key = 2^256 mogelijkheden — onhaalbaar |
|
||||||
| **Sniffen** | Pakketten zijn onversleuteld; geen confidentiality. Alleen commando's (aan/uit) zijn zichtbaar, niet de key. |
|
| **Sniffen** | Pakketten zijn onversleuteld; geen confidentiality. Alleen commando's (aan/uit) zijn zichtbaar, niet de key. |
|
||||||
| **Reverse engineering van key** | Sleutel staat niet in de APK — wordt pas bij USB provisioning gegenereerd en in SharedPreferences opgeslagen. Extractie vereist fysieke toegang tot de telefoon én root. |
|
| **Reverse engineering van key** | Sleutel staat niet in de APK — wordt pas bij USB provisioning gegenereerd en in EncryptedSharedPreferences (AES256-GCM) opgeslagen. Extractie vereist fysieke toegang tot de telefoon én root. |
|
||||||
| **Key extractie via USB** | Fysieke USB-toegang tot de ESP geeft `KEY?\n` de huidige key terug. Bescherming: fysieke toegang tot het board beperken. |
|
| **Key extractie via USB** | Fysieke USB-toegang tot de ESP geeft `KEY?\n` de huidige key terug. Bescherming: fysieke toegang tot het board beperken. |
|
||||||
| **NVS wiping** | `pio run -t erase` wist alle flash waaronder NVS — ESP is daarna leeg en kan opnieuw geprovisioneerd worden. |
|
| **NVS wiping** | `pio run -t erase` wist alle flash waaronder NVS — ESP is daarna leeg en kan opnieuw geprovisioneerd worden. |
|
||||||
|
|
||||||
|
|
@ -235,21 +235,26 @@ De firmware binaries worden via `tools/copy_firmware.sh` naar de app assets geko
|
||||||
- **Write Without Response:** Gebruikt `WRITE_TYPE_NO_RESPONSE` voor minimale latency — ON en OFF kunnen direct achter elkaar worden verstuurd zonder ACK-wachttijd
|
- **Write Without Response:** Gebruikt `WRITE_TYPE_NO_RESPONSE` voor minimale latency — ON en OFF kunnen direct achter elkaar worden verstuurd zonder ACK-wachttijd
|
||||||
- **MTU negotiatie:** Vraagt MTU 256 aan na service discovery (voor 37-byte HMAC packets)
|
- **MTU negotiatie:** Vraagt MTU 256 aan na service discovery (voor 37-byte HMAC packets)
|
||||||
- **HMAC authenticatie:** Elk commando wordt ondertekend met HMAC-SHA256 + sequence counter
|
- **HMAC authenticatie:** Elk commando wordt ondertekend met HMAC-SHA256 + sequence counter
|
||||||
- **USB auto-provisioning:** Bij aansluiten van de ESP via USB-OTG leest de app de bestaande key uit, of genereert een nieuwe key als de ESP er nog geen heeft. Beide scenario's tonen de toast _"Beveiligingssleutel gelezen"_
|
- **USB provisioning via Instellingen:** Via Instellingen → Sleutel Uitwisselen leest de app de bestaande key uit, of genereert een nieuwe key als de ESP er nog geen heeft. Beide scenario's tonen de toast _"Beveiligingssleutel gelezen"_
|
||||||
- **Firmware flashen via app:** De app kan de ESP32 firmware rechtstreeks via USB flashen (download-modus), geen computer nodig
|
- **Firmware flashen via app:** De app kan de ESP32 firmware rechtstreeks via USB flashen (download-modus), geen computer nodig
|
||||||
- **Instellingenscherm:** Losse Activity voor sleutel uitwisseling, firmware flashen, en buildnummer weergave
|
- **Instellingenscherm:** Losse Activity voor sleutel uitwisseling, firmware flashen, en buildnummer weergave
|
||||||
- **Permissies:** Vraagt BLUETOOTH_SCAN, BLUETOOTH_CONNECT en ACCESS_FINE_LOCATION aan (Android 12+)
|
- **Permissies:** Vraagt BLUETOOTH_SCAN, BLUETOOTH_CONNECT en ACCESS_FINE_LOCATION aan (Android 12+). Bij weigeren wordt de knop uitgeschakeld en wordt de gebruiker doorverwezen naar Instellingen
|
||||||
|
- **BLE write feedback:** `onCharacteristicWrite` callback logt succes/falen; bij falen wordt "Communicatiefout" getoond
|
||||||
|
- **GATT status logging:** Verbindingsstatus, service discovery en MTU-negotiatie loggen foutstatussen (`GATT_SUCCESS` check)
|
||||||
|
- **Scan annulering:** BLE-scan wordt betrouwbaar gestopt in `onPause` via gedeelde `scanRunnable` + `removeCallbacks`
|
||||||
|
- **Cancellable firmware flash:** Flash-proces kan geannuleerd worden via de annuleerknop; `EspFlasher` gebruikt een `@Volatile cancelled` flag
|
||||||
|
- **Key exchange robuustheid:** 5 retries (was 3), 1000ms delay (was 500ms), duidelijkere foutmelding bij falen
|
||||||
|
|
||||||
### Instellingenscherm
|
### Instellingenscherm
|
||||||
|
|
||||||
1. Open de app
|
1. Open de app
|
||||||
2. Tik op het **tandwiel-icoon** rechtsboven
|
2. Tik op het **tandwiel-icoon** rechtsboven
|
||||||
3. De app toont twee acties: **Sleutel Uitwisselen** en **Firmware Flashen**
|
3. De app toont twee acties: **Sleutel Uitwisselen** en **Firmware Flashen**
|
||||||
4. **Sleutel Uitwisselen:** Sluit de ESP via USB-OTG aan. De app leest de key uit (als de ESP er al een heeft) of genereert een nieuwe key en provisioneert de ESP. Toast: _"Beveiligingssleutel gelezen"_
|
4. **Sleutel Uitwisselen:** Sluit de ESP via USB-OTG aan. De app leest de key uit (als de ESP er al een heeft) of genereert een nieuwe key en provisioneert de ESP. Bij falen: toast _"Sleutel uitwisselen mislukt — probeer opnieuw"_. Bij succes: _"Beveiligingssleutel gelezen"_
|
||||||
5. **Firmware Flashen:** Zet de ESP in download-modus (houd reset 2s in tot groene LED). Sluit aan via USB. De app flasht de firmware. Dialog verdwijnt automatisch bij loskoppelen USB.
|
5. **Firmware Flashen:** Zet de ESP in download-modus (houd reset 2s in tot groene LED). Sluit aan via USB. De app flasht de firmware met voortgangsbalk. Het flash-proces kan geannuleerd worden. Dialog verdwijnt automatisch bij loskoppelen USB.
|
||||||
6. Onderaan staat het **Buildnummer** (timestamp van de build)
|
6. Onderaan staat het **Buildnummer** (timestamp van de build)
|
||||||
|
|
||||||
De key wordt opgeslagen in SharedPreferences en overleeft app-herstarts. Bij geen key toont de hoofdapp "Geen HMAC sleutel - open Instellingen" en wordt er niet gescant.
|
De key wordt opgeslagen in EncryptedSharedPreferences (AES256-GCM versleuteld) en overleeft app-herstarts. Bij geen key toont de hoofdapp "Geen HMAC sleutel - open Instellingen" en wordt er niet gescant.
|
||||||
|
|
||||||
### Hoofdscherm UI
|
### Hoofdscherm UI
|
||||||
|
|
||||||
|
|
@ -261,8 +266,9 @@ De key wordt opgeslagen in SharedPreferences en overleeft app-herstarts. Bij gee
|
||||||
|
|
||||||
Vereisten:
|
Vereisten:
|
||||||
- Android SDK (API 34+)
|
- Android SDK (API 34+)
|
||||||
- Java 17+ (Java 26 werkt niet met Gradle — gebruik Java 21)
|
- Java 21 (Java 26 werkt niet met Gradle)
|
||||||
- Gradle 8.14 (via wrapper)
|
- Gradle 8.14 (via wrapper)
|
||||||
|
- AGP 8.5.2, Kotlin 2.0.21 (K2 compiler)
|
||||||
- USB host support (raw Android USB API, geen externe serial library)
|
- USB host support (raw Android USB API, geen externe serial library)
|
||||||
- PlatformIO (voor firmware compilatie)
|
- PlatformIO (voor firmware compilatie)
|
||||||
|
|
||||||
|
|
@ -273,7 +279,7 @@ bash tools/copy_firmware.sh
|
||||||
|
|
||||||
# App bouwen en installeren
|
# App bouwen en installeren
|
||||||
cd android
|
cd android
|
||||||
JAVA_HOME=/pad/naar/jdk21 ./gradlew installDebug
|
JAVA_HOME=/home/linuxbrew/.linuxbrew/opt/openjdk@21/libexec ./gradlew installDebug
|
||||||
```
|
```
|
||||||
|
|
||||||
De APK staat na build in `android/app/build/outputs/apk/debug/app-debug.apk`.
|
De APK staat na build in `android/app/build/outputs/apk/debug/app-debug.apk`.
|
||||||
|
|
@ -281,14 +287,7 @@ De APK staat na build in `android/app/build/outputs/apk/debug/app-debug.apk`.
|
||||||
### Installeren
|
### Installeren
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
adb install -r android/app/build/outputs/apk/debug/app-debug.apk
|
adb -s <ip>:5555 install -r android/app/build/outputs/apk/debug/app-debug.apk
|
||||||
```
|
|
||||||
|
|
||||||
Of via draadloze foutopsporing:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
adb connect <ip>:<poort>
|
|
||||||
adb install -r android/app/build/outputs/apk/debug/app-debug.apk
|
|
||||||
```
|
```
|
||||||
|
|
||||||
## Eerste keer setup
|
## Eerste keer setup
|
||||||
|
|
@ -302,7 +301,7 @@ adb install -r android/app/build/outputs/apk/debug/app-debug.apk
|
||||||
2. **App op de telefoon installeren:**
|
2. **App op de telefoon installeren:**
|
||||||
```bash
|
```bash
|
||||||
cd android
|
cd android
|
||||||
JAVA_HOME=/pad/naar/jdk21 ./gradlew installDebug
|
JAVA_HOME=/home/linuxbrew/.linuxbrew/opt/openjdk@21/libexec ./gradlew installDebug
|
||||||
```
|
```
|
||||||
|
|
||||||
3. **Firmware flashen (kies één):**
|
3. **Firmware flashen (kies één):**
|
||||||
|
|
@ -357,17 +356,18 @@ sylvia-touch-relay/
|
||||||
│ ├── build.gradle # App module config (esp32-flash-lib, minSdk 26)
|
│ ├── build.gradle # App module config (esp32-flash-lib, minSdk 26)
|
||||||
│ └── src/main/
|
│ └── src/main/
|
||||||
│ ├── AndroidManifest.xml
|
│ ├── AndroidManifest.xml
|
||||||
│ ├── assets/firmware/ # GeCompileerde firmware binaries
|
│ ├── assets/firmware/ # Gecompileerde firmware binaries
|
||||||
│ │ ├── bootloader.bin
|
│ │ ├── bootloader.bin
|
||||||
│ │ ├── partitions.bin
|
│ │ ├── partitions.bin
|
||||||
│ │ ├── firmware.bin
|
│ │ ├── firmware.bin
|
||||||
│ │ └── boot_app0.bin
|
│ │ └── boot_app0.bin
|
||||||
│ ├── java/com/eddy/atomrelay/
|
│ ├── java/com/eddy/atomrelay/
|
||||||
│ │ ├── MainActivity.kt # BLE client + HMAC signing
|
│ │ ├── MainActivity.kt # BLE client + HMAC signing + write feedback
|
||||||
│ │ ├── SettingsActivity.kt # Key exchange + firmware flash UI
|
│ │ ├── SettingsActivity.kt # Key exchange + firmware flash UI
|
||||||
│ │ ├── EspFlasher.kt # ESP32 firmware flasher via USB
|
│ │ ├── EspFlasher.kt # Cancellable ESP32 firmware flasher via USB
|
||||||
│ │ ├── UsbSerialTransport.kt # USB serial transport voor flasher
|
│ │ ├── UsbSerialTransport.kt # USB serial transport voor flasher
|
||||||
│ │ ├── UsbUtils.kt # Gedeelde USB utility functions
|
│ │ ├── UsbUtils.kt # Gedeelde USB utility functions
|
||||||
|
│ │ ├── PrefsHelper.kt # EncryptedSharedPreferences utility (AES256-GCM)
|
||||||
│ │ └── BuildConfig # Buildnummer (timestamp)
|
│ │ └── BuildConfig # Buildnummer (timestamp)
|
||||||
│ └── res/
|
│ └── res/
|
||||||
│ ├── drawable/
|
│ ├── drawable/
|
||||||
|
|
@ -376,7 +376,9 @@ sylvia-touch-relay/
|
||||||
│ │ └── ic_settings.xml # Tandwiel icoon
|
│ │ └── ic_settings.xml # Tandwiel icoon
|
||||||
│ ├── layout/
|
│ ├── layout/
|
||||||
│ │ ├── activity_main.xml # Hoofd UI (FrameLayout)
|
│ │ ├── activity_main.xml # Hoofd UI (FrameLayout)
|
||||||
│ │ └── activity_settings.xml # Key exchange + flash UI
|
│ │ ├── activity_settings.xml # Key exchange + flash UI
|
||||||
|
│ │ ├── dialog_waiting.xml # Spinner dialog (vervangt ProgressDialog)
|
||||||
|
│ │ └── dialog_progress.xml # Voortgangsbalk dialog (vervangt ProgressDialog)
|
||||||
│ └── values/strings.xml
|
│ └── values/strings.xml
|
||||||
├── .gitignore
|
├── .gitignore
|
||||||
└── README.md
|
└── README.md
|
||||||
|
|
@ -385,14 +387,19 @@ sylvia-touch-relay/
|
||||||
## Technische notities
|
## Technische notities
|
||||||
|
|
||||||
- De Atom VoiceS3R gebruikt de ES8311 audio codec op G45/G0/G48/G4/G3/G17/G11, IR TX op G47, en de knop op G41. Deze pinnen zijn niet beschikbaar voor eigen gebruik.
|
- De Atom VoiceS3R gebruikt de ES8311 audio codec op G45/G0/G48/G4/G3/G17/G11, IR TX op G47, en de knop op G41. Deze pinnen zijn niet beschikbaar voor eigen gebruik.
|
||||||
- G1 en G2 zitten op de HY2.0-4P Grove poort. G5-G8, G38, G39 zitten op de onderste expantie-header.
|
- G1 en G2 zitten op de HY2.0-4P Grove poort. G5-G8, G38, G39 zitten op de onderste expansie-header.
|
||||||
- De firmware gebruikt geen M5Unified library — alleen Arduino core + ESP32 BLE + mbedtls. Dit maakt de serial output betrouwbaarder via USB CDC.
|
- De firmware gebruikt geen M5Unified library — alleen Arduino core + ESP32 BLE + mbedtls. Dit maakt de serial output betrouwbaarder via USB CDC.
|
||||||
- `ARDUINO_USB_CDC_ON_BOOT=1` is ingeschakeld voor virtuele serial via USB.
|
- `ARDUINO_USB_CDC_ON_BOOT=1` is ingeschakeld voor virtuele serial via USB.
|
||||||
- De HMAC key wordt opgeslagen in NVS (Non-Volatile Storage) via de Arduino `Preferences` library. De key wordt niet meegecompileerd maar via USB provisioning door de Android app op de ESP gezet. Sleuteloverdracht werkt in beide richtingen: de app kan een key naar de ESP schrijven (nieuwe ESP) of een bestaande key uitlezen (bestaande ESP op nieuwe telefoon).
|
- De HMAC key wordt opgeslagen in NVS (Non-Volatile Storage) via de Arduino `Preferences` library. De key wordt niet meegecompileerd maar via USB provisioning door de Android app op de ESP gezet. Sleuteloverdracht werkt in beide richtingen: de app kan een key naar de ESP schrijven (nieuwe ESP) of een bestaande key uitlezen (bestaande ESP op nieuwe telefoon).
|
||||||
- De app gebruikt `WRITE_TYPE_NO_RESPONSE` (Write Without Response) voor minimale latency. De ESP characteristic heeft `PROPERTY_WRITE_NR` zodat writes zonder ACK worden geaccepteerd. Dit maakt directe ON/OFF sequenties mogelijk zonder write queue.
|
- De app gebruikt `WRITE_TYPE_NO_RESPONSE` (Write Without Response) voor minimale latency. De ESP characteristic heeft `PROPERTY_WRITE_NR` zodat writes zonder ACK worden geaccepteerd. Dit maakt directe ON/OFF sequenties mogelijk zonder write queue.
|
||||||
- De sequence counter start bij 1 (niet 0) om te voorkomen dat het eerste packet na connectie als replay wordt geweerd (`0 <= 0` is true in de `seq <= lastSeq` check).
|
- De sequence counter start bij 1 (niet 0) om te voorkomen dat het eerste packet na connectie als replay wordt geweerd (`0 <= 0` is true in de `seq <= lastSeq` check).
|
||||||
- Voor meerdere ESP modules met verschillende keys: sluit elke ESP één voor één aan op de telefoon. De app genereert per ESP een eigen key en slaat deze op. De app ondersteunt momenteel slechts één key tegelijk (opgeslagen in SharedPreferences). Bij wisselen van ESP: wis app data of sluit de nieuwe ESP aan (overschrijft vorige key).
|
- Voor meerdere ESP modules met verschillende keys: sluit elke ESP één voor één aan op de telefoon. De app genereert per ESP een eigen key en slaat deze op. De app ondersteunt momenteel slechts één key tegelijk (opgeslagen in EncryptedSharedPreferences). Bij wisselen van ESP: wis app data of sluit de nieuwe ESP aan (overschrijft vorige key).
|
||||||
- De app gebruikt raw Android USB API (`UsbDeviceConnection.bulkTransfer`) in plaats van een externe serial library. Dit vermijdt compatibiliteitsproblemen met de ESP32-S3 USB-JTAG/serial controller die geen CDC `get_status` requests ondersteunt.
|
- De app gebruikt raw Android USB API (`UsbDeviceConnection.bulkTransfer`) in plaats van een externe serial library. Dit vermijdt compatibiliteitsproblemen met de ESP32-S3 USB-JTAG/serial controller die geen CDC `get_status` requests ondersteunt.
|
||||||
- CDC control interface setup: line coding (115200 8N1) via SET_LINE_CODING request + DTR/RTS via SET_CONTROL_LINE_STATE. Dit activeert de data-flow op de ESP32-S3 CDC-ACM interface voordat commando's worden gestuurd.
|
- CDC control interface setup: line coding (115200 8N1, parity NONE) via SET_LINE_CODING request + DTR/RTS via SET_CONTROL_LINE_STATE. Dit activeert de data-flow op de ESP32-S3 CDC-ACM interface voordat commando's worden gestuurd.
|
||||||
- Het buildnummer wordt via `BuildConfig.BUILD_NUMBER` in `build.gradle` gegenereerd als timestamp (`yyyyMMddHHmmss`). Elke build produceert een uniek buildnummer dat onderaan het instellingenscherm staat.
|
- Het buildnummer wordt via `BuildConfig.BUILD_NUMBER` in `build.gradle` gegenereerd als timestamp (`yyyyMMddHHmmss`). Elke build produceert een uniek buildnummer dat onderaan het instellingenscherm staat.
|
||||||
- `minSdk` is 26 (Android 8.0). Android 7 en lager worden niet ondersteund.
|
- `minSdk` is 26 (Android 8.0). Android 7 en lager worden niet ondersteund.
|
||||||
|
- ProgressDialog is vervangen door eigen `AlertDialog` layouts (`dialog_waiting.xml` met spinner, `dialog_progress.xml` met ProgressBar). ProgressDialog is deprecated sinds API 26.
|
||||||
|
- De HMAC key wordt in de app opgeslagen via `EncryptedSharedPreferences` (AES256-GCM) via `PrefsHelper`. Dit versleutelt de key at-rest, in tegenstelling tot gewone SharedPreferences waarbij waarden plaintext op schijf staan.
|
||||||
|
- Bij geweigerde BLE-permissies wordt de HOLD-knop uitgeschakeld en toont de app "Permissies geweigerd — open Instellingen" met een link naar de systeem-instellingen.
|
||||||
|
- De BLE-scan wordt in `onPause` betrouwbaar gestopt via `handler.removeCallbacks(scanRunnable)`, wat voorkomt dat scans doordraaien op de achtergrond.
|
||||||
|
- Het firmware flash-proces is annuleerbaar: `EspFlasher` gebruikt een `@Volatile cancelled` flag met checkpoints in de flash-lus. `SettingsActivity.onDestroy` roept `flasher?.cancel()` aan.
|
||||||
Loading…
Reference in a new issue