Files
elmer/doc/android-bugs-2026-05-25.md
T

4.6 KiB
Raw Blame History

Android-клиент: баги найденные при ревизии (2026-05-25)

Коммиты с исправлениями: d57336a (предыдущий раунд) → 7f9b83c (этот раунд).


Баг 1 — КРИТИЧЕСКИЙ: NetworkOnMainThreadException при подключении

Что было неверно

onStartCommand вызывался на главном (UI) потоке. Из него напрямую вызывались btConnect() и tcpConnect().

Внутри btConnect():

btSocket?.connect()   // ← БЛОКИРУЮЩИЙ вызов на main thread

Внутри tcpConnect():

tcpSocket = Socket(host, port)  // ← БЛОКИРУЮЩИЙ вызов на main thread

Почему это баг

Android с версии 3.0 (Honeycomb, API 11) запрещает сетевые операции на главном потоке. Если нарушение — NetworkOnMainThreadException и краш при запуске. Даже если бы не падало — UI зависал бы на время соединения (ANR через 5 секунд).

Как исправлено

Оба вызова обёрнуты в thread { }:

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-режима:

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):

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() при накоплении байт из устройства:

} 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 внутри многострочного ответа — он остаётся. Сервер должен быть устойчив, но лучше не давать мусор.

Как исправлено

} else if (c != '\r' && c != '\n') sb.append(c)

Итог по файлам

Файл Изменение
ElmForwardService.kt btConnect/tcpConnect → в thread {}, скип \n
MainActivity.kt TCP-режим → URL из IP устройства + порт 5005