doc: android client bugs found and fixed 2026-05-25

This commit is contained in:
“Naeel”
2026-05-25 23:18:04 +04:00
parent 6a1af162e2
commit 806c49dbd6
+110
View File
@@ -0,0 +1,110 @@
# Android-клиент: баги найденные при ревизии (2026-05-25)
Коммиты с исправлениями: `d57336a` (предыдущий раунд) → `7f9b83c` (этот раунд).
---
## Баг 1 — КРИТИЧЕСКИЙ: NetworkOnMainThreadException при подключении
### Что было неверно
`onStartCommand` вызывался на **главном (UI) потоке**. Из него напрямую вызывались `btConnect()` и `tcpConnect()`.
Внутри `btConnect()`:
```kotlin
btSocket?.connect() // ← БЛОКИРУЮЩИЙ вызов на main thread
```
Внутри `tcpConnect()`:
```kotlin
tcpSocket = Socket(host, port) // ← БЛОКИРУЮЩИЙ вызов на main thread
```
### Почему это баг
Android с версии 3.0 (Honeycomb, API 11) запрещает сетевые операции на главном потоке. Если нарушение — **`NetworkOnMainThreadException`** и краш при запуске. Даже если бы не падало — UI зависал бы на время соединения (ANR через 5 секунд).
### Как исправлено
Оба вызова обёрнуты в `thread { }`:
```kotlin
if (debug != null) {
val p = debug.split(":")
thread(name = "ElmConnect", isDaemon = true) {
tcpConnect(p[0], p.getOrNull(1)?.toIntOrNull() ?: 35000)
}
} else {
intent.getStringExtra(EXTRA_DEVICE_MAC)?.let { mac ->
thread(name = "ElmConnect", isDaemon = true) { btConnect(mac) }
}
}
```
`isDaemon = true` — поток не удерживает JVM, умирает вместе с сервисом.
---
## Баг 2 — КРИТИЧЕСКИЙ: TCP-режим слал данные на obdai.ru вместо локального сервера
### Что было неверно
В `MainActivity`, в ветке TCP-режима:
```kotlin
putExtra(ElmForwardService.EXTRA_SERVER_URL, "https://obdai.ru/api/v1/raw-obd")
```
URL захардкожен на продакшн-сервер.
### Почему это баг
TCP-режим предназначен для **теста без ELM327**: mock ELM327 (`tools/mock_elm327.py`) и Flask-сервер (`web/app.py`) запускаются на одной машине в локальной сети. Поле URL в UI в этом режиме занято адресом mock-устройства (`192.168.X.X:35000`), поэтому у пользователя нет способа указать другой сервер.
Результат: телефон подключается к локальному mock по TCP, но OBD-данные уходят на `obdai.ru` — тест ничего не проверяет.
### Как исправлено
Сервер выводится автоматически из того же хоста, что и устройство, на стандартный порт Flask (5005):
```kotlin
val deviceHost = debugHost.split(":")[0]
val localServerUrl = "http://$deviceHost:5005/api/v1/raw-obd"
```
Итого: ввёл `192.168.1.42:35000` → mock на `:35000`, сервер на `http://192.168.1.42:5005`.
---
## Баг 3 — МАЛЫЙ: символ `\n` попадал в raw-строку
### Что было неверно
В `loop()` при накоплении байт из устройства:
```kotlin
} else if (c != '\r') sb.append(c)
```
Фильтровался только `\r`, но не `\n`.
### Почему это баг
ELM327 отвечает в формате `DATA\r\n>`. Разделитель ответа — `>`. Код правильно использует `>` как признак конца пакета, но `\n` перед `>` или внутри многострочного ответа накапливается в `sb`.
Пример: ответ `4 1 05 4B\r\n>` → в буфере будет `4 1 05 4B\n` вместо `4 1 05 4B`. `.trim()` при отправке на сервер снимает крайние `\n`, но если `\n` внутри многострочного ответа — он остаётся. Сервер должен быть устойчив, но лучше не давать мусор.
### Как исправлено
```kotlin
} else if (c != '\r' && c != '\n') sb.append(c)
```
---
## Итог по файлам
| Файл | Изменение |
|------|-----------|
| `ElmForwardService.kt` | `btConnect`/`tcpConnect` → в `thread {}`, скип `\n` |
| `MainActivity.kt` | TCP-режим → URL из IP устройства + порт 5005 |