MifareNFC
Автономный Android-инфотерминал для систем контроля доступа и учета посещений, который считывает карты и браслеты стандарта Mifare Classic, выводит баланс или статус владельца на полноэкранный киоск-интерфейс и управляет физическими исполнительными устройствами: турникетами, электронными шкафами и реле по интерфейсу Wiegand. Терминал рассчитан на круглосуточную работу без оператора, целиком настраивается на месте через встроенную веб-панель и поддерживает два независимых канала чтения карт сразу: штатный NFC-модуль Android и внешний промышленный считыватель, подключенный по последовательному порту.
Задача
Требовался единый программный продукт, который одинаково работает на недорогих промышленных терминалах с внешними считывателями и на обычных Android-устройствах со встроенным NFC, при этом закрывает весь цикл от прикладывания карты до срабатывания замка. Ключевые требования:
- Чтение нескольких несовместимых схем кодирования карт, которые используют разные объекты эксплуатации (баланс, идентификатор, служебные поля, статус заряда браслета).
- Управление физическим доступом через Wiegand и релейные выходы GPIO с обратной связью по датчикам.
- Бесперебойная работа в режиме 24/7 с автозапуском после перезагрузки и защитой от вмешательства пользователя.
- Полная настройка на объекте без участия программиста и без пересборки приложения.
- Ведение журнала проходов с выгрузкой в файл и в облако.
Решение
В основу легла гибридная архитектура: нативное Android-ядро отвечает за оборудование и системные политики, а весь пользовательский интерфейс вынесен в локальное веб-приложение внутри WebView и управляется конфигурацией в формате JSON. Такой подход позволил менять оформление, тексты, цвета, медиаконтент и карту чтения полей прямо на объекте, не трогая APK.
Слой чтения карт спроектирован абстрактно и закрывает два взаимозаменяемых аппаратных бэкенда одним общим контрактом. Поверх него реализованы три алгоритма декодирования, включая вариант с вычислением ключа сектора прямо из UID карты по XOR-схеме. Связка с контролем доступа построена на формировании кодов Wiegand 26 и 34 и управлении реле через GPIO с опросом состояния датчиков. Поверх всего этого выстроена киоск-оболочка с режимом владельца устройства, блокировкой задачи и автостартом, которая превращает обычный планшет в защищенный терминал единственного назначения.
Архитектура и сложности
Двусторонний мост между нативным кодом и веб-интерфейсом. Java-ядро и JavaScript обмениваются данными в обе стороны: интерфейс вызывает методы устройства через JavascriptInterface, а ядро обновляет экран, передавая готовые фрагменты разметки и переменные обратно в WebView. Это дало живой настраиваемый UI без тяжелого нативного слоя верстки и без обращений к сети.
Собственная реализация бинарного протокола последовательного порта. Внешний считыватель работает через нативную библиотеку serial port (JNI, сборки под armeabi, armeabi-v7a и x86) и аппаратный SDK. Поверх сырого потока реализован разбор кадров с контролем целостности по BCC, а также пошаговая последовательность команд: активация поля, антиколлизия, выбор карты, аутентификация сектора и чтение блока. Обмен вынесен в отдельный поток с очередью, чтобы не блокировать UI и не терять кадры при частых прикладываниях.
Надежность для режима без оператора. Состояние GPIO опрашивается каждые 100 мс для мгновенной реакции замка и датчиков, а последовательный порт автоматически переоткрывается каждые 5 минут, что устраняет зависания драйвера при долгой непрерывной работе. Логика чтения целиком обернута в защиту от исключений: единичный сбой считывания не роняет терминал, а возвращает понятное сообщение и готовность к следующей карте.
Поддержка нескольких форматов карт без правок кода. Карта чтения (сектор, блок, диапазон байтов, способ конвертации и ключ) хранится как конфигурируемая структура и сериализуется в JSON. Один и тот же терминал обслуживает балансовые карты, идентификационные пропуска и браслеты с телеметрией заряда, переключаясь между схемами через настройки.
Жесткая киоск-блокировка. Приложение регистрируется как владелец устройства, использует Lock Task Mode и режим COSU, перехватывает кнопку HOME, стартует после загрузки системы, скрывает системные панели и работает в полноэкранном иммерсивном режиме. Пользователь физически не может выйти из терминала или получить доступ к системе.
Журналирование и облако. События проходов с отметкой времени и статусом заряда браслета накапливаются и выгружаются: локально в выбранную папку через Storage Access Framework либо в облачное хранилище по токену, что обеспечивает удаленный сбор статистики с распределенной сети терминалов.
Результат
- Один программный продукт закрывает оба сценария эксплуатации: встроенный NFC и внешний промышленный считыватель, без отдельных сборок.
- Три алгоритма декодирования и конфигурируемая карта полей покрывают весь парк используемых карт и браслетов, включая работу со всеми 40 секторами Mifare Classic.
- Время от прикладывания карты до результата на экране удерживается в пределах секунды за счет таймаутов команд порядка 100 мс и потоковой обработки.
- Терминал работает в режиме 24/7 с автоматическим восстановлением порта и автозапуском после перезагрузки, что исключает выезды для ручного перезапуска.
- Полная настройка внешнего вида, текстов, медиаконтента, схемы чтения и параметров доступа выполняется на объекте через веб-панель и переносится между устройствами импортом и экспортом конфигурации, без обновления приложения.
- Интеграция Wiegand 26 и 34 с релейным управлением и обратной связью по датчикам обеспечила прямое подключение к турникетам и электронным шкафам существующей инфраструктуры.
Стек
Android (Java, минимальный SDK 24, целевой 34), WebView с двусторонним мостом JavaScript и Java, фронтенд на HTML, CSS, jQuery. Работа с оборудованием: Android NFC API (Mifare Classic), нативная библиотека последовательного порта через JNI и NDK, аппаратный SDK считывателя, протокол Wiegand и управление GPIO и реле. Системный уровень: Device Owner, Device Policy Manager, Lock Task Mode и режим COSU, автозапуск по BOOT_COMPLETED, оверлеи и полноэкранный иммерсивный режим. Данные и интеграции: сериализация в JSON через Gson, хранение в SharedPreferences, Storage Access Framework, выгрузка журналов в облачное хранилище. Сборка на Gradle Kotlin DSL с подписанием релиза.
