Mi band api android

История реверс-инженеринга китайского фитнес-браслета

Купи китайский браслет, разочаруйся в официальном ПО, напиши свое!

Эта история ждала своей публикации больше полугода, за это время многое изменилось, прошивки и ПО обновились и многие мои наработки уже устарели.

Предисловие

Активная работа большого количества компаний в области носимой техники и умных часов не оставляла покоя моей душе. Я видел в носимых устройствах с экраном большой потенциал. Нет, я не говорю о подсчете шагов и других фитнес-штуках, они безусловно классные, но пока кроме банальных «Поздравляем! Вы прошли 4км, сделали 20к+ шагов!» и красивых графиков прогресса и регресса, ничего особенного не придумали.
А вот то, что я могу получать уведомления прямо на дисплей на запястье — это удобно. Если я могу еще и как-то взаимодействовать с ним или с чем-то поблизости нажимая 1-2-3 кнопки — это еще круче.

В очередной раз бороздя просторы aliexpress, я наткнулся на фитнес-браслет iWown i5. Он сразу привлек моё внимание невероятно низкой ценой ( на тот момент около 800р с бесплатной доставкой ) и наличием OLED дисплея. Внимательно почитав описание продавца и отзывы покупателей, я решил заказать сие чудо.

Заявленные характеристики (перевод описания с aliexpress):

  • Дисплей: OLED
  • Батарея: литий-полимерная
  • Зарядка: стандартная USB зарядка
  • Работа в режиме ожидания: более 72-х часов
  • Размеры: 69.1*15.8*11.2mm
  • Вес: 18g
  • Материал: Ремешок из ABS, стальная застежка
  • Водонепроницаемость: IP55
  • Рабочая температура: -20 ° C

+ 45 ° C
Рабочая температура флеш носителя: -40 ° C

Возможности:

  • Спортивный монитор: все время записывает шаги и движения, пройденное расстояние и сожженные калории, все цифры рассчитываются с учетом Вашего веса и роста.
  • Мониторинг качества сна: Пока Вы спите, трекер записывает фазы сна, определяя глубокий и быстрый сон, 8 групп бесшумных будильников позволяют будить Вас не тревожа других членов семьи
  • Bluetooth 4.0 low-power беспроводная синхронизация
  • Поддержка синхронизации с PC через USB
  • Защита IP55: защищает устройство под сильным дождем, но не более

и другие «надуманные» плюсы в стиле китайского маркетинга

Меня сильно заинтересовала возможность трекать сон и будить в нужную фазу. Многие мои знакомые покупали недорогие фитнес трекеры именно из-за этой функции и были довольны mi band и тому подобными штуками. Мне в них всегда не хватало экрана, а тут все-в-одном.
В моей работе частенько приходиться разрабатывать простые приложения для Android, я решил, что если мне не хватит функционала родного приложения, напишу своё.

Посылка пришла довольно быстро и я тут же бросился изучать замечательный браслет. После часа игры с приложением Zeroner, которое по инструкции необходимо поставить на свой Android девайс, я понял, что функционал довольно скуден и печален. Zeroner как и все остальные производители делал акцент на подсчет шагов и калорий, выводя красивые графики, имеет функцию поиска телефона (об этом позже расскажу), может оповещать о входящем вызове, о приходе сообщения в facebook и whatsapp и пересылает уведомления с ОДНОГО любого выбранного приложения, которое будет считать как приложение для SMS.
Вибрация у браслета весьма спорная, на форумах пишут что слабовата, некоторые говорят, нормальная. По мне так, можно было бы и по сильнее. У браслета есть реакция на жест «Посмотреть на часы», если посмотреть на браслет как на наручные часы, поднимая руку и сгибая в локте, автоматически включится экран и покажет время или пропущенное уведомление.

В общем, не долго думая, я решил писать своё приложение, с уведомлениями, вибрацией и синхронизацией. Забегу вперед, на это ушло 4 выходных дня и несколько долгих вечеров…

К делу

Учитывая что с Bluetooth я не в-синий-зуб-ногой, с дуру решил попытаться перехватить данные, которыми обменивается телефон и браслет. Для этого я полез во вкладку для разработчиков, и включил галку «Включить журнал трансляции HCI Bluetooth». После включения этой опции, весь дамп общения андроида с любыми Bluetooth устройствами складывается в файл /sdcard/Android/data/btsnoop_hci.log (у разных устройств путь может меняться, имя файла вроде всегда одинаковое).
Скачав WireShark я принялся изучать логи общения с браслетом и увидел что-то похожее на это:

Проведя почти два часа, изучая логи, проводя зависимости, гугля в интернете протоколы, я понял, что такой путь не для меня.

Так как мой телефон все-же интерпретировал браслет как обычное BLE устройство и показывал его в разделе подключенных устройств, я решил воспользоваться примерами работы с BLE из Android SDK.
Склонировав репозиторий https://github.com/googlesamples/android-BluetoothLeGatt, натравил Android Studio на пупку с исходниками, собрал и запустил приложение. (Ссылка на описание работы Android SDK с Bluetooth LE)

Получилось как на картинках с гитхаба:

Запустив сканирование, приложение не увидело устройство. Оказалось, что родное приложение подключившись к браслету не давало BLE найти устройство. Все решилось простым удалением Zeroner, можно было просто отключить, но надежнее снести полностью.

И так, Bluetooth LE — это технология которая строится на устройствах с малым потреблением энергии, используется в новомодных датчиках, метках и многих других устройствах. Основой этой технологии служит Generic Attribute Profile (GATT), это Bluetooth профиль, позволяющий обмениваться маленькими порциями данных, «атрибутами». Не буду долго расписывать как это все работает, на хабре и в инете есть куча информации, которую мне также пришлось перерыть в поисках решений.

Я понадеялся, что все нужные мне данные хранятся в характеристиках и дескрипторах браслета, и я смогу получать и записывать данные безо всяких проблем. Я ошибался…

Тестовое BLE приложение показывало мне всего 4 сервиса:
0000180f-0000-1000-8000-00805f9b34fb
00001800-0000-1000-8000-00805f9b34fb
0000ff20-0000-1000-8000-00805f9b34fb
00001801-0000-1000-8000-00805f9b34fb

в них было очень мало характеристик, те, что читались, возвращали пустоту или нули, а писать было бесполезно. Но меня воодушевило то, что я смог подключиться и получить хоть какие-то данные.

Далее, я решил, что в слепую действовать не получиться и решил препарировать приложение Zeroner. Накопав в интернете пару онлайн APK декомпиляторов, я скормил им zeroner.apk и получил на выходе 2 zip архива.
Первый был JADX вариант, а воторой содержал результат работы apktool.

Роясь в исходниках я ужасался китайскому коду (хотя в работе я часто с ним сталкиваюсь в виде бэендов для сайтов и сервисов, но он не перестает удивлять своей извилистостью и изобретательностью, но как ни крути, он ужасно тяжело читается)
После долгих изысканий, я наконец наткнулся на файл WristBandDevice.java, который находился по пути com.kunekt/bluetooth.
В этом классе как раз и скрывалась вся работа с устройством, но опять таки, меня ждала засада.
Как позже выяснилось, в предыдущих прошивках браслета использовалось больше сервисов в характеристик (как я ранее и предполагал), но позже, разработчики оставили всего 2, одна на чтение, вторая на запись. Все команды передаются в одном пакете.

Понять как должен выглядеть пакет оказалось не так просто, я решил четко определиться, чего я хочу от браслета в первую очередь, что бы начать прослеживать вызовы функций. А хотел я, отображать кастомные сообщения на браслете.
Не долго думая, я полез в com.kunekt/receiver/CallReceiver.java, так как входящие вызовы отображались очень стабильно и даже русскими символами, я решил что это отличное начало, учитывая что я уже сталкивался с событием входящих вызовов в Android, представление о том, как это может работать уже было.

Открыв файл я увидел это:

Тут мы явно видим, что существует 2 варианта API и названия у них очень логичные newAPI, а второе соответственно oldAPI. Во всем этом обилии условий, меня заинтересовала только одна, повторяющаяся строка:
WristBandDevice.getInstance(context).writeWristBandPhoneAlertNew(context, contact.getDisplayName. )

Это было то самое, что я искал. Забегая вперед, скажу, что у iWown есть еще модели i5+ и i6, у них экран больше и соответственно символов помещается больше, для этого и нужны все эти проверки. непонятно почему они не написали класс или что-то вроде того, возможно это шалости декомпелятора, но данный код повторяется во многих местах.
Перейдя к определению этой функции, я увидел это:

Отлично, используется одна и та же функция для отправки текста, просто с разными параметрами. Все функции со словом New — это как раз наш вариант, потому что как выяснилось выше, API у меня new.

Радостно перейдя к определению функции writeAlertNew, я увидел следующее:

Было понятно, что от профита меня отделяет пара функций, которые используются здесь.
writeWristBandDataByte — формирует пакет с сообщением для браслета, интересно, что есть специальная функция form_Header(3, 1), которая формирует заголовок пакета, по которому браслет понимает чего от него хотят. 3 — это номер группы команд, а 1 — это сама команда

Функция простая, скопировал себе в проект без изменений. Следующее было это

NewAgreementBackgroundThreadManager.getInstance().addTask(new WriteOneDataTask(context, writeData));

Как оказалось, ничего необычного, приложение создает поток, в котором постоянно проверяется очередь пакетов на отправку, если в очереди появляется пакет, поток выполняет запись в заданную характеристику устройства, если пакетов больше чем один, она отправляет их с задержкой в 240 миллисекунд.
Далее шло самое непонятное:

PebbleBitmap.fromString(context, String.valueOf(e), 8, 1).data)

Почему класс называется именно так, непонятно, ведь с Pebble у данного устройства нет ничего общего. Открыв исходник класса я увидел следующее:

После долгого осмысления я пришел к выводу, что fromString создает картинку с буквой используя определенный шрифт (который вшит в приложение), а потом конвертирует пиксели в 0 или 1 в зависимости от заполнения, таким образом, буква О, будет выглядеть примерно так:
00011100
01100011
01100011
01100011
00011100

Не особо вникая в подробности, я скопировал все в свой проект использовав BLE GATT пример от гугла.
И… О чудо. Браслет завибрировал! Но вот сообщение не отобразилось, пустая строка и значок входящего вызова.
Оказалось, что куча проверок размеров не спроста, браслет тупо игнорит черезчур длинные сообщения и сообщения, длина которых 11 символов, хотя 12 отображает нормально. Пару часов танцев вокруг этих функций наконец дали результат, я научился отображать и русский и английский текст, а заодно узнал, что в группе сообщений есть несколько режимов работы:

  1. Входящий вызов. Отображается трубка, имя звонящего и браслет вибрирует
  2. Сообщение. Отображается текст и значок конверта. При появлении вибрирует 2 раза
  3. Облачко. Тоже самое что и 2, только вместо конвертика, иконка облачка
  4. Ошибка. Тоже что и 2, что только иконка с восклицательным знаком.

Научив своё приложение пересылать мне уведомления от разных приложений, whatsapp, vk, viber, telegram и других, я решил, что пора научить браслет реагировать на входящие вызовы и уже, в конце-концов, задействовать единственную кнопку для сброса входящих.

Описывать этот процесс не буду, пост и так получился раздутым, скажу лишь, что реагировать на входящие оказалось не сложно, а вот задействовать кнопку — нет.

Все входящие сообщения от браслета, Zeroner перехватывал в специальном классе. входящий пакет имел заголовок группы команд и номер команды, после долгого дебага и тестов я выудил используемые группы, а потом нашел описание в коде Zeroner.

// HEADER GROUPS //
DEVICE = 0
CONFIG = 1
DATALOG = 2
MSG = 3
PHONE_MSG = 4

// CONFIG = 1 ///
CMD_ID_CONFIG_GET_AC = 5
CMD_ID_CONFIG_GET_BLE = 3
CMD_ID_CONFIG_GET_HW_OPTION = 9
CMD_ID_CONFIG_GET_NMA = 7
CMD_ID_CONFIG_GET_TIME = 1

CMD_ID_CONFIG_SET_AC = 4
CMD_ID_CONFIG_SET_BLE = 2
CMD_ID_CONFIG_SET_HW_OPTION = 8
CMD_ID_CONFIG_SET_NMA = 6
CMD_ID_CONFIG_SET_TIME = 0

// DATALOG = 2 //
CMD_ID_DATALOG_CLEAR_ALL = 2
CMD_ID_DATALOG_GET_BODY_PARAM = 1
CMD_ID_DATALOG_SET_BODY_PARAM = 0

CMD_ID_DATALOG_START_GET_DAY_DATA = 3
CMD_ID_DATALOG_START_GET_MINUTE_DATA = 5
CMD_ID_DATALOG_STOP_GET_DAY_DATA = 4
CMD_ID_DATALOG_STOP_GET_MINUTE_DATA = 6

// DEVICE = 0 //
CMD_ID_DEVICE_GET_BATTERY = 1
CMD_ID_DEVICE_GET_INFORMATION = 0
CMD_ID_DEVICE_RESE = 2
CMD_ID_DEVICE_UPDATE = 3

// MSG = 3 //
CMD_ID_MSG_DOWNLOAD = 1
CMD_ID_MSG_MULTI_DOWNLOAD_CONTINUE = 3
CMD_ID_MSG_MULTI_DOWNLOAD_END = 4
CMD_ID_MSG_MULTI_DOWNLOAD_START = 2
CMD_ID_MSG_UPLOAD = 0

// PHONE_MSG = 4 //
CMD_ID_PHONE_ALERT = 1
CMD_ID_PHONE_PRESSKEY = 0

Благодаря этому, я смог реализовать полноценную работу с браслетом. Могу получать данные о шагах, о сне. Могу управлять настройками, ставить будильники. Обозначение байт самого пакета удалось достать из классов, сохраняющих данные в БД, все их я реализовал и у себя.

В итоге

Немного подумав, я решил, что все это может пригодиться не только мне и написал новое приложение, которое содержит в себе все необходимые данные и функции для работы с браслетом, а так же реализует простой интерфейс для переправки оповещений от любого приложения на браслет.

С тех пор прошло много времени, и у многих после обновления до Android 6, приложение перестало работать. Оно так же не стабильно работает с прошивками браслетов 2-й версии. Но я надеюсь найти время на доработку.

Исходный код выложен на GitHub. Можно форкать и развлекаться как угодно. Все pull-request после review будут приниматься, и после тестов сразу же заливаться на Google Play.

На данный момент приложение умеет:

  • Отображать уведомления от любого приложения
  • Отображать входящий звонок
  • Сбрасывать входящий при нажатии на кнопку
  • Искать телефон если он в зоне действия BT
  • Управлять настройками браслета
  • И некоторые другие мелкие функции

Реализовано подключение к Google Fit для сохранения данных о тренировках, но, как я не ковырял SDK к Fit, перерыл кучу ссылок и форумов, но так и не понял, как заставить фит отображать данные с кастомных устройств. Непонятно тогда, зачем эта функция вообще есть.
Если кто-то работал с Google Fit, и знает как заставить его использовать данные с кастомного сенсора для отображения графиков, расскажите в коментах или напишите мне, пользователи и я будем очень благодарны!

Так же была идея, подключить браслет к Sleep as Adnroid. Собственно для мониторинга сна и покупался браслет. Но, как оказалось, iWown умеет возвращать только продолжительность фаз сна. То есть уже посчитанные данные с акселерометра.
А Sleep as Android требует голые данные с акселерометра, причем с желательной периодичностью в 10 секунд.

В общем итоге. Приглашаю разработчиков и владельцев поддержать проект своим кодом, советами и чем угодно. Оставляйте pull-requist, делайте issue на Github.
Приложение оказалось очень популярно за рубежом, мне часто пишут иностранцы, просят что-то добавить/исправить/перевести.

Кстати, у iWown i5 есть несколько клонов, со схожими прошивками:
Vidonn X5
Harper BFB-301
Excelvan i5

Источник

Читайте также:  Как сделать трансляцию экрана андроид
Оцените статью