BLE под микроскопом (ATTы GATTы. )
BLE под микроскопом (ATTы GATTы. )
Уже прошло довольно большое время, с тех пор, когда вышла первая спецификация на Bluetooth 4.0. И, хотя тема BLE очень интересна, она до сих пор отталкивает многих разработчиков, из-за своей сложности. В своих предыдущих статьях я рассматривал в основном самый нижний уровень Link Layer и Physical Layer. Это позволяло не обращаться к таким сложным и запутанным понятиям как протокол атрибутов(ATT) и общий профиль атрибутов (GATT). Однако деваться некуда, не понимая их, невозможно разрабатывать совместимые устройства. Сегодня я хотел бы поделиться с вами этими знаниями. В своей статье я буду опираться на учебник для начинающих с сайта Nordic-а. Итак, давайте приступим.
Почему всё так сложно?
На мой взгляд, сразу было понятно, что управление устройствами через смартфоны — тема очень перспективная и долгоиграющая. Поэтому её решили структурировать сразу и по-максимуму. Что бы производители различных гаджетов не придумывали свои протоколы, которые потом будут не совместимы. Отсюда и сложность. Уже на первом этапе в протокол BLE попытались втиснуть всё что только было возможно. И не важно пригодится это в последствии или нет. Кроме того, предусмотрели возможность расширения списка устройств на будущее.
Давайте взглянем на картинку, где нарисована схема протокола BLE. Он состоит из нескольких слоев. Самый нижний, физический слой (PHY) отвечает за радиоканал устройства. Link Layer(LL) содержит всю последовательность байтов в передаваемом сообщении. В прошлых статьях мы изучали именно его. Host Controller Interface (HCI) — это протокол обмена между слоями или микросхемами BLE, если Controller и Host реализованы на разных чипах. За формирование пакетов, деление на кадры, контролем ошибок и сборку пакетов отвечает Logical Link Control and Adaptation Protocol (L2CAP). За шифрование пакетов отвечает Security Manager Protocol (SMP). Профиль общего доступа (GAP) отвечает за первоначальный обмен данными между устройствами, для определения «Who is who». К нему так же относятся scanning и advertising. В этой статье я остановлюсь на двух оставшихся частях протокола — GATT и ATT. GATT является надстройкой над ATT, поэтому они сильно переплетены.
Для упрощения повествования, я бы хотел обратиться к аналогии. Я её где то слышал и хотел бы поддержать. Представьте себе BLE устройство в качестве книжного шкафа с несколькими полками. Каждая полка — это отдельная тематика. К примеру, у нас есть полки с фантастикой, математикой, энциклопедиями. На каждой полке стоят книги с указанной темой. А в некоторых книгах есть даже бумажные закладки с записями. Кроме того, у нас есть небольшой бумажный каталог всех книг. Если помните школьные библиотеки — это узкий ящик с бумажными карточками. При такой аналогии шкаф — это профиль нашего устройства. Полки — это сервисы, книги — характеристики, а каталог — это таблица атрибутов. Закладки в книгах — это дескрипторы, о которых я так же расскажу позже, более подробно.
Все, кто разрабатывал устройства, знает, что во многих проектах есть похожие куски кода. Дело в том, что многие устройства имеют сходный функционал. К примеру, если устройства работают от аккумуляторов, то проблема зарядки и контроля их уровня, будет одинаковой. То же касается и датчиков. Собственно, объектно-ориентированный подход в программировании «предоставляет возможность создавать объекты, которые соединяют свойства и поведения в самостоятельный союз, который затем можно многоразово использовать». На мой взгляд, в BLE была предпринята попытка похожего подхода. Группой Bluetooth Special Interest Group (SIG) были разработаны профили. Устройства от разных производителей, имеющие одинаковые профили, должны без труда работать друг с другом. Профили в свою очередь состоят из сервисов, а сервисы из характеристик, дополняемых дескрипторами. В общем случае это может выглядеть так:
Для примера, рассмотрим схему профиля heart rate monitor (фитнес браслет). Он состоит из двух сервисов и нескольких характеристик. Из неё сразу становится понятна иерархия профиля. Характеристика контрольной точки сбрасывает общий подсчет расходов калорий в ноль.
1. Сервис сердечного ритма включает три характеристики (0x180D):
a) Обязательная характеристика частоты сердечных сокращений (0х2A37)
б) Опционная характеристика положения датчика тела (0x2A38)
в) Условная характеристика контрольной точки сердечного ритма (0x2A39)
2. Сервис обслуживания батареи (0x180F):
a) Обязательная характеристика уровня заряда батареи (0x2A19)
Для того, чтобы мы могли однозначно обращаться к элементам профиля (сервисам, характеристикам и дескрипторам) нужно все их как то пронумеровать. Для этой цели вводят такое понятие как Universally Unique ID (UUID) или Универсальный уникальный идентификатор. В скобках каждой строки как раз и указан UUID. И здесь есть одна особенность. Для UUID решили использовать код длиной 16 и 128 бит. Зачем, спросите вы? В протоколе BLE всё подчинено сохранению энергии. Поэтому размерность в 16 бит вполне разумна. Вряд ли в ближайшем будущем будет создано больше 65тыс. уникальных сервисов и характеристик. На данный момент всё что могли, уже посчитали (помните откуда это — «он и вас посчитал» :-)) Пронумерованные элементы профилей, сервисов, характеристик и дескрипторов вы можете посмотреть по ссылкам.
Однако, я думаю, все помнят историю с 4-мя байтами IPv4 адреса в интернете. Сначала думали что хватит, а теперь всё никак не перейдем на IPv6 адресацию. Что бы не повторять этой ошибки и дать простор шаловливым ручкам самодельщиков, SIG сразу решила ввести ещё и 128-ми битные UUID. Это лично мне напоминает нелицензионный диапазон 433МГц, который был дан на откуп всевозможным Кулибиным от радиоканала. В нашем случае, на откуп был отдан 128-ми битный идентификатор сервисов и характеристик. Это означает что мы, для своих сервисов и устройств, можем использовать практически любое 128 битное значение. Всё равно вероятность придумать одинаковый UUID стремится к нулю.
На самом деле, короткие 16-ти битные UUID имеют своё расширение до 128-ми битного значения. В спецификации это расширение называется Bluetooth Base UUID и имеет значение 00000000-0000-1000-8000-00805F9B34FB. Если, к примеру, 16-ти битный UUID аттрибута имеет значение 0x1234, то эквивалентный ему 128-ми битный UUID будет иметь значение 00001234-0000-1000-8000-00805F9B34FB. И даже приводится соответствующая формула:
128_bit_value = 16_bit_value * 2^96 + Bluetooth_Base_UUID
Откуда взялось это магическое число, мне не известно. Если кто нибудь из читателей знает — пусть напишет в комментариях (Пользователь с ником Sinopteek уже сделал это. Смотрите комментарии). Что касается придумывания 128-ти битных UUID, то в принципе можно воспользоваться специальным генератором, который сделает это за вас.
ATTы GATTы.
Собственно дальше начинается самое интересное. Я напомню, что ATT основан на клиентском-серверном отношении. Сейчас мы рассматриваем устройство сервера. Он содержит такую информацию, как значения датчиков, состояние выключателя света, данные о местоположении и т.д. Теперь, когда все «участники нашего парада» пронумерованы, надо как то их размещать в памяти устройства. Для этого мы помещаем их в таблицу, которая называется таблицей атрибутов. Запомните это хорошенько. Это самое сердце BLE. Именно его мы и будем рассматривать в дальнейшем. Теперь каждую строчку мы будем называть аттрибутом. Эта таблица находится в глубине стека и, как правило, мы не имеем к ней прямого доступа. Мы её инициализируем и к ней обращаемся, но что там происходит внутри, от нас скрыто за семью печатями.
Рассмотрим картинку из спецификации, однако перед этим, хочу сразу обратить внимание на частую путаницу в терминах, а именно в дескрипторах. Роль дескриптора — дополнить описание характеристики. Когда надо расширить её возможности, тогда и применяют дескрипторы. Они так же являются аттрибутами, и так же, наравне с сервисами и характеристиками, располагаются в таблице аттрибутов. Мы подробно разберем их во второй части статьи. Однако иногда дескрипторами называют номер строки в таблице аттрибутов. Это надо иметь ввиду. Мы же, что бы не путаться, будем для этих целей использовать термин «указатель атрибута».
Итак атрибут — это дискретное значение, которое имеет следующие свойства, связанные с ним:
1. Указатель атрибута (Attribute Handle) — это индекс таблицы, соответствующий атрибуту
2. Тип атрибута (Attribute Type) — это UUID, который описывает его тип
3. Значение аттрибута (Attribute Value) — это данные, индексируемые указателем атрибута
4. Разрешения атрибутов (Attribute Permissions) — это часть атрибута, разрешения, которые не могут быть прочитаны или записаны с использованием протокола атрибутов
Как всё это понимать? Указатель аттрибута — это, условно говоря, его номер в нашей таблице.
Он позволяет клиенту ссылаться на атрибут в запросах чтения или записи. Мы можем нумеровать наши строчки (аттрибуты) от 0x0001 до 0xFFFF. В нашей ассоциации с книжным шкафом — это номер карточки в бумажном каталоге. Аналогично, как в каталоге библиотеки, карточки располагаются в порядке увеличения номера. Номер каждой последующей строчки должен быть больше предыдущей. Как и в библиотеке, иногда теряются некоторые карточки, так и у нас — в нумерации строк могут быть промежутки. Это допускается. Главное, что бы они шли по нарастающей.
Тип атрибута определяет что представляет собой данный атрибут. По аналогии с языком Си,
где есть булевые, числовые переменные и строки, так и здесь. По типу аттрибута мы узнаем
с чем мы имеем дело и как нам дальше с этим аттрибутом работать. Ниже мы рассмотрим некоторые специфичные типы аттрибутов. Например «сервисная декларация» (0х2800), «декларация характеристики» (0х2803), «декларация дескриптора» (0x2902).
Как это выглядит
Концепция GATT заключается в том, чтобы сгруппировать атрибуты в таблице атрибутов вместе в очень специфическом и логическом порядке. Давайте более внимательно рассмотрим профиль частоты сердечных сокращений, приведенный ниже. Самый левый столбик этой таблицы не обязательный. Он просто описывает нам чем является эта строчка (аттрибут). Все остальные столбцы нам уже знакомы.
В верхней части каждой группы мы всегда имеем атрибут объявления сервиса. Его тип всегда равен 0x2800, а указатель зависит от того, сколько атрибутов уже присутствует в таблице. Его разрешения всегда доступны только для чтения, без какой-либо проверки подлинности или авторизации. Об этих понятиях мы поговорим чуть позже. Значение — это еще один UUID, определяющий, что это за служба. В Таблице значение равно 0x180D, что определяется Bluetooth SIG как сервис частоты сердечных сокращений.
Вслед за объявлением сервиса, следует объявление характеристики. По форме оно аналогично объявлению сервиса. Его UUID всегда имеет значение 0x2803, а разрешения так же всегда доступны только для чтения без какой-либо проверки подлинности или авторизации. Давайте посмотрим на поле Attribute Value, которое включает некоторые данные. Оно всегда содержит указатель, UUID и набор свойств. Эти три элемента описывают последующее объявление значения характеристики. Указатель естественным образом обозначает место объявления значения характеристики в таблице атрибутов. UUID описывает, какой тип информации или значения мы можем ожидать. Например, значение температуры, состояние выключателя света или какое-либо другое произвольное значение. И наконец свойства, которые описывают, как можно взаимодействовать с характеристическим значением.
Тут нас поджидает ещё один подводный камень. Он связан с разрешениями аттрибутов и свойствами характеристик. Давайте мы посмотрим на картинку свойств битового поля из спецификации.
Как видите, здесь так же присутствуют поля, дающие возможности чтения и записи. Вы можете задаться вопросом, почему у нас есть разрешения на чтение/запись для атрибута и свойства
чтения/записи для значения характеристики? Разве они не должны быть всегда одинаковыми? Дело в том, что свойства для значения характеристики, фактически являются только рекомендациями для клиента, используемыми в ГАТТ и прикладных слоях. Это просто подсказки о том, что клиент может ожидать от атрибута объявления характеристики. Давайте с этим разберемся поподробнее. Какие виды разрешений существуют у аттрибута?
1. Разрешения доступа:
— чтение
— запись
— чтение и запись
2. Разрешение аутентификации:
— аутентификация требуется
— аутентификация не требуется
3. Разрешение авторизации:
— авторизация требуется
— авторизация не требуется
Главное отличие разрешения аттрибутов от свойств характеристик состоит в том, что первые относятся к серверам, а вторые к клиентам. У сервера может быть разрешено чтение значения характеристики, но при этом стоять требование аутентификации или авторизации. Поэтому при запросе клиентом свойств характеристики, мы получим, что чтение разрешено. Но при попытке чтения получим ошибку. Поэтому можно смело говорить о приоритете разрешений над свойствами. Знание о том, какие разрешения есть у аттрибута, мы, со стороны клиента, получить не можем.
Дескриптор
Вернемся к нашей таблице. После объявления значения характеристики, возможны следующие объявления аттрибутов:
1. Новое объявление характеристики (в сервисе может быть много характеристик)
2. Новая декларация сервиса (в таблице может быть их много)
3. Объявление дескриптора
В случае характеристики измерения частоты сердечных сокращений, в нашей таблице, объявление значения характеристики сопровождается объявлением дескриптора. Дескриптор — это атрибут с дополнительной информацией о характеристике. Существует несколько видов дескрипторов. О них мы подробно поговорим во второй части этой статьи. Сейчас же мы коснемся только дескриптора конфигурации характеристик клиента (Client Characteristic Configuration Descriptor — CCCD). Он имеет UUID равную 0х2902. При помощи этого дескриптора клиент имеет возможность включить на сервере индикацию или нотификацию. Разница между ними небольшая, но всё таки есть. Нотификация не требует подтверждения в получении со стороны клиента. Индикация же этого требует, хотя она и происходит на уровне GATT, не доходя до уровня приложения. Зачем так, спросите вы? Увы, это мне не ведомо. Скажу лишь, что специалисты Nordic-а рекомендуют использовать нотификацию. Тем более, что проверка целостности пакета (при помощи CRC) происходит в обоих случаях.
Заключение
В конце статьи я хотел бы сказать вот о чем. Последняя таблица несколько запутанна. Однако я остановился на ней из-за того, что она приводится в статье, на которую я опираюсь. Во второй части своей статьи я намерен углубиться в спецификацию BlueTooth 4.0. Там нас ждут более корректные схемы и рисунки. В третьей части, я хотел бы разобрать лог, полученный при помощи программы Wireshark от одного из гаджетов и увидеть «в живую» всю ту теорию, которую мы с вами изучаем.
Сотрудник Группы Компаний «Цезарь Сателлит»
Печерских Владимир
Беспроводные технологии передачи звука на базе Bluetooth: что же лучше?
С развитием технологий так привычные всем «ламповые» аналоговые наушники уходят в историю – их всё больше вытесняют беспроводные собратья на базе Bluetooth.
Современные смартфоны лишаются привычного разъёма в угоду влаго- и пылезащищённости.
Разработчики выпускают всё новые версии протокола Bluetooth и всё новые версии кодеков, обещая «быстрее, выше, сильнее» — меньшие задержки в воспроизведении и лучшее качество.
Настолько ли всё хорошо? Давайте посмотрим.
Введение
Я не буду углубляться в техническую реализацию протоколов, а также в скучные спецификации. Уважаемый ValdikSS, который в большой степени выступил вдохновителем и даже научным консультантом в этой статье, готовит исчерпывающий материал касательно кодеков – и там всё будет изложено куда более подробно и технически верно.
Рассказать я хочу в большей степени о личном опыте. Ну и немножко занимательной (скучной?) практики.
Полтора года назад я загорелся идеей aptX. Да, я прочитал массу обзоров наподобие этого и поверил во все эти технические навороты и возможности. Родился ребёнок – и очень хотелось ночью с женой смотреть передачи в наушниках, не создавая шума и не будя никого в доме.
Во что же это вылилось?
Качество
Начнём с цифр и фактов (привет, Википедия!)
SBC – старый добрый кодек, обязательный при соблюдении стандарта A2DP. Кодек – результат работы Франса де Бонта (F. de Bont, M. Groenewegen and W. Oomen, «A High Quality Audio-Coding System at 128 kb/s», 98th AES Convention, Febr. 25-28, 1995) и использования алгоритмов, описанных в патенте EP-0400755B1. Примечательно, что авторы патента разрешают бесплатное использование SBC только в приложении Bluetooth, тем не менее патент истёк 2 июня 2010 года. Поскольку стандарт A2DP весьма распространён, крайне трудно найти наушники или колонки, в которых не было бы поддержки SBC.
Кодек обеспечивает частоту дискретизации 16, 32, 44.1, 48 кГц со скоростью потока 10-1500 кбит/с. Да, Вы не ослышались. До 1500 кбит/с. В кодеке просто нет лимита по битрейту. Но об этом – позже.
При разрядности 16 бит и частоте дискретизации 48 кГц кодек может обеспечивать скорость потока 384 кбит/с (dual channel).
Список продуктов, официально поддерживающих aptX. На Aliexpress можно найти очень много неведомых систем с поддержкой aptX, но будьте готовы к тому, что на поверку там окажется тот же старый добрый SBC – и не более.
aptX HD – тот же самый кодек, но с другим профилем кодирования, имеет скорость потока 576 кбит/с, поддержку частоты дискретизации до 48 кГц и разрядность до 24 бит. Некоторые называют этот кодек aptX Lossless – но это полный бред хотя бы потому, что в настоящий момент невозможно достигнуть значения потока, который мог бы переносить lossless-данные. Особым преимуществом этого кодека регулируемая задержка кодирования, которая может снижаться до 1 мс при частоте дискретизации 48 кГц. Также кодек крайне выгоден с позиции загрузки процессора, в чём выражается преимущество по сравнению с МР3 и ААС.
aptX Low latency (или LL) – специальная версия кодека, позволяющая снизить время задержки звука до менее 40 мс. Список продуктов, официально поддерживающих aptX LL.
Вот она. Именно эта картинка в своё время купила меня с потрохами. Задержки! Ведь кому захочется слышать звук взрыва в каком-то боевике, крик монстра в ужастике или рёв толпы на футбольном матче, когда всё уже закончилось?
Но так ли всё это на самом деле?
Как и в любом маркетинговом материале, цифры притянуты за уши. Задержка во многом зависит от буферизации в системе и реализации кодека. Так, задержка с SBC вполне может быть на уровне менее 40 мс, что принимая во внимание стандарты телевизионного вещания (+40 мс… −60 мс) – вполне допустимо.
Практические и очень субъективные итоги
Информация базируется на уже упомянутом полуторагодовом опыте эксплуатации, сравнения и привлечения сторонних слушателей.
Опыт базируется на прослушивании lossless на плеере SONY Walkman NWZ-A17, где кодек можно выбирать, а также на просмотре различных передач с выводом звука посредством Avantree Priva III.
Наушников было трое: Sennheiser PMX 60, Koss Porta Pro и Koss UR-20.
В качестве приёмников беспроводного сигнала использовались Jabra BT3030 (SBC) и Avantree Clipper Pro (aptX).
Также использовалась колонка Voombox Outdoor (SBC) и наушники с костной проводимостью Aftershokz Trekz Titanium (aptX).
Все эквалайзеры и улучшатели отключались — и это важно.
Не спорю, что если подключать всяческие спектральные анализаторы и прочее — можно и нужно увидеть разницу. Но человеческое ухо, а ещё хуже — среднестатистическое человеческое ухо — не спектральный прибор, а потому все эти нюансы не слышит.
Практика: исправляем то, что можно исправить
Часть 1. Включаем aptX
Как уже было сказано, в некоторых устройствах использование aptX отключено, вероятно, во избежание патентных преследований.
Этот вопрос можно решить достаточно просто – подсунув устройству библиотеки для реализации работы кодека и прописав возможность работы с этим кодеком в build.prop.
На просторах интернета имеется большое количество решений такого характера. Я взял на себя смелость объединить их в одно, при этом реализовав в виде модуля для Magisk. Да, я очень люблю этот проект и считаю, что реализация изменений в системе в виде модулей Magisk – лучше и безопасное решение с возможностью сохранения системы максимально в первозданном виде и лёгким способом отката назад.
Модуль можно скачать отсюда. Да, я знаю про гитхаб. И нет, пока у меня нет времени туда его выкладывать.
Записи в build.prop, включающие aptX, а при возможности – и aptX HD, будут эмулироваться модулем автоматически.
Часть 2. Повышаем битрейт SBC
Как уже сообщалось, кодек SBC принципиально не имеет ограничений по битрейту. Однако производители обычно ставят ограничение в 342 кбит/с для моно- и 345 кбит/с для стереосигнала с целью обспечения надёжной работы со всеми типами принимающих устройств.
При этом спецификация A2DP v1.2, которая была активна с 2007 по 2015 год, предписывает всем декодирующим устройствам корректно работать с битрейтами до 320 кбит/с для моно- и 512 кбит/с в случае стереосигнала.
В новой версии спецификации ограничение по битрейту отсутствует вообще. Предполагается, что современные наушники, выпущенные после 2015 года и поддерживающие EDR, могут поддерживать битрейты до 730 кбит/с.
По факту это конечно же не так. В обширном исследовании, проведённом ValdikSS, было найдено, что практически все приёмные устройства надёжно работают со значениями битрейта 454 кбит/с, и достаточно большое количество – с битрейтом 507 кбит/с.
В своих исследованиях ValdikSS также показал, что, вопреки расхожему мнению о качестве звука кодека aptX, на некоторых файлах он может давать результаты хуже, чем SBC со стандартным битрейтом в 328 кбит/с, а переключившись в высокобитрейтный SBC, Вы получите звук, зачастую превосходящий aptX, на любых наушниках.
ValdikSS на основании этих данных направил замечания разработчикам Lineage OS и в Google, но на данный момент никакой реакции не последовало.
Таким образом, нам остаётся только вручную внести модификации в Bluetooth-стек.
Нам потребуется IDA Pro с возможностью декомпилирования ARM, любой HEX-редактор (я использовал WinHEX) и файл bluetooth.default.so с нашего устройства. Обычно он находится по пути /system/lib/hw и реже – ещё и по пути /system/lib64/hw (безусловно нужен рут-доступ).
Итак, открываем файл bluetooth.default.so Описанные ниже операции и модификации применимы только к оригинальному стеку Android (bluedroid). Если вы видите строку «Needed Library ‘com.qualcomm.qti.bluetooth_audio@1.0.so’» или подобную в IDA Pro, с большой вероятностью, эта инструкция вам не поможет.
Наша первая задача — заменить Joint Stereo на Dual Channel в стандартной конфигурации.
В итоге довольно быстро мы находим саму функцию в виде кода:
Наша задача – подменить исходную структуру проверок
Это можно сделать несколькими способами.
Первый – подмена инструкций TST.W R0, #1 на TST.W R0, #4 и MOVS R0, #1 на MOVS R0, #4 в последовательности проверок:
В байт-коде это замена х01 на х04. При этом важно отметить характерные последовательности байтов, по которым можно найти этот паттерн. Не углубляясь сильно в детали, скажу, что по сути это поиск последовательности
Однако у данного способа есть недостатки.
Ряд компиляторов меняют последовательность исполнения команд в зависимости от оптимизации. И в таком случае найти искомый паттерн не удаётся, а иногда механизм проверок в структуре вообще вносится в инлайн-код. Поэтому более надёжным является изменение константы btif_av_sbc_default_config.
Для начала – найдём её. Она в самом начале нашей функции, ведь
Это позволит изменить логику работы структуры аналогичным образом, но при этом оптимизации компилятора не доставят проблем.
В ряде случаев инициатором подключения выступают сами наушники или колонки. В этом случае режим определяется функцией bta_av_co_audio_init.
Функция характерна строкой «bta_av_co_audio_init: %d» и легко отыскивается в коде:
Перечисление возможных режимов подключения выполняется в следующей команде:
Константа легко находится в коде, в моём случае она равна 20 0F F0 0C 03 35 02:
Обратите внимание на байт 0F – он обеспечивает возможность подключения с любым из допустимых режимов, поскольку
Наша задача — изменить это значение таким образом:
Следовательно, необходимо заменить
Итак, мы заставили стек подключаться в режиме Dual Channel как в случае инициирования подключения устройством, так и в случае инициирования подключения принимающей сигнал стороной.
Теперь необходимо убрать ограничения в битрейте либо повысить его верхний порог.
Необходимо обработать btif_media_task_get_sbc_rate. Аналогичным образом по поиску характерной строки «non-edr a2dp sink detected, restrict rate to %d» отыскиваем функцию в коде:
Ограничение битрейта выражается в строке
UINT16 rate = DEFAULT_SBC_BITRATE (что в свою очередь равно 328 кбит/с)
Изменим это значение на 454 кбит/с – это выше стандартного и работает с подавляющим большинством приёмных устройств. Для этого заменим байты
Также следует выполнить поиск по паттерну
Значение Е3 может быть другим в зависимости от желаемое максимального битрейта:
На практике стоит поэкспериментировать и выбрать максимальное значение, при котором во всех Ваших принимающих устройствах происходит стабильный приём сигнала, отсутствует треск, прерывания и искажения.
На всех приёмниках в моём эксперименте (выше я их указал) таким значением было 576 кбит/с, в качестве источника сигнала выступал телефон Xiaomi Redmi 4x MIUI10 Android 7.1.
На основе описанных действий был создан дженерик-патч, который находит указанные паттерны в Bluetooth.default.so и заменяет их? включая принудительно режим Dual Channel и устанавливая лимит битрейта в 454 кбит/с. При необходимости, значение лимита может быть легко изменено на основании поиска и замены соответствующего байта — внимательный читатель сделает это без труда.
Подчёркиваю: патч работает только в случае bluedroid-стека и скорее всего не будет успешным в случае стека Fluoride и версии Android 8 и новее.
Патч можно скачать отсюда.
Замену оригинального файла настоятельно рекомендуется выполнять в виде модулей Magisk, для себя я это сделал следующим образом. Обратите внимание: данные модули сделаны мной для телефона Xiaomi Redmi 4x 3/32 Гб с актуальной на момент написания статьи стоковой глобальной стабильной прошивкой MIUI 10. В Вашем случае файл bluetooth.default.so придётся заменить на свой собственный, пропатченный, как описано выше. Возможно также, что файл придётся продублировать по пути /system/lib64/hw — это зависит от модели и версии прошивки Вашего телефона.
Данный подход с использованием модулей Magisk позволяет легко изменять максимальный битрейт и вообще отключать изменения, если окажется, что какое-то из принимающих устройств не поддерживает Dual Channel.
Заключение
В настоящий момент в погоне за продажами многие компании подают некоторые технологические новинки как обоснование более высокой цены.
На практике оказывается, что уже имеющиеся, более дешёвые технологии не до конца развиты, а технологические новинки – не до конца внедрены, что значительным образом сказывается на качестве.
Очень часто пользователи испытывают «эффект плацебо», убеждая себя в совершенстве того или иного продукта только потому, что он новее или более красочно преподнесён. По факту, это качество оказывается мнимым.
Несмотря на очевидное ухудшение качества беспроводной передачи звука по сравнению с проводным вариантом, по-видимому, современные производители устройств нацелены на полный переход к беспроводным технологиям. При этом для обоснования повышения цены используются маркетинговые уловки: защита от погружения в воду телефона (как можно разговаривать под водой? зачем ронять устройство в воду?), использование более дорогих кодеков и т.д. При этом потенциал имеющегося популярного кодека SBC до конца не освоен.
Нам не удалось ни получить разъяснений от Google касательно лимита битрейта в 328 кбит/с, ни добиться устранения этого лимита и добавления опции включения Dual Channel в меню Bluetooth от разработчиков Lineage OS.
Спасибо всем, кто дочитал до конца!
Некоторое продолжение, вызванное обсуждением в комментариях и пробелом в отношении кодека LDAC, находится здесь.





