Как заставить Android устройства с радио на atheros/qualcomm нормально мигрировать (в большинстве случаев).

android wifi roaming

Сначала хорошая новость. Достаточно давно atheros/qualcomm добавили в свои драйвера вполне внятную логику handover (миграции внутри плоской L2 сети, с точки зрения клиента,  с множественными AP, на которых установлен один SSID). Собственно, тот самый роуминг, да ещё и бесшовный (ну если используемые AP не совсем плохи и умеют моментально пропихивать в опорную сеть критичные данные, например, ARP-ы для обновления arp таблиц на устройствах в сети + ещё ряд условий на тему кривости).

Теперь по проблеме. Handover есть, сеть с нормальными AP есть, но чтобы клиент мигрировал, по-прежнему требуется пинок, иначе висит до последнего… Что делать и кто виноват? И вот тут, по ходу рассмотрения крутилок, я это проясню.

Для продолжения нужно само устройство, обязательно рутованное, обязательно использующее радио на чипе qualcomm (например yota phone 2).

Перемонтируем разделы в RW, идём в /system/etc/wifi, видим там файлик WCNSS_qcom_cfg.ini – собственно, это основной конфигурационный файл, читаемый драйвером wifi.

Драйверы QCA сами реализуют слои SME/MLME, не экспортируя эти функции на плечи wpa_supplicant. И вся логика управления подключениями и миграцией возложена на них. Wpa_supplicant в Android собран с NO_ROAMING, а значит можно ожидать, что это сделано так же у всех абсолютно чипмэйкеров для Android (конфиги, естественно, разные, или же, как у Broadcom, интересующие нас параметры к исправлению задаются при компиляции, и изменить их на лету невозможно).

Первая крутилка, которая нас будет интересовать в конфиге, это:

# default value of this parameter is zero to enable dynamic threshold allocation
# to set static roming threshold uncomment below parameter and set vaule
gNeighborLookupThreshold=78

Эта крутилка напрямую отвечает за то, когда клиент начнёт пытаться искать кандидата (форсирует сканирование, или запросит список ближайших AP по RRM и проведёт короткую процедуру скана для подтверждения).  И значение у нас тут -78дБ… Как работает автоматика (когда в 0 выставлено значение), надо будет разобраться, как будет время.

И вроде бы всё хорошо, но… Мы как обычно забыли, что то, что слышит клиент и то, что слышит AP, может отличаться по уровню на десятки дБ… Обычно в android-клиентах передатчик в 20МГц полосе может выдать 16дБм, в 40МГц в лучшем случае 14дБм. Тогда как со стороны AP обычно имеем как минимум 20дБм дури даже в 40МГц полосе (обычно определяется законодательными ограничениями конкретной страны, для РФ 20дБм в 2.4ГГц и 23дБм в 5ГГц независимо от ширины, хоть в 80МГц эти 23дБм, хоть в 20МГц). Из опыта, при таком раскладе в среднем перекос по уровням в прямой видимости уже в 10 метрах будет составлять около 10-15Дб, а если есть преграды, то запросто перевалит и за 30.

Но возьмём 10дБ для простоты. Т.е. когда клиент видит AP с уровнем в -78дБ, AP видит клиента уже с уровнем около -88дБ. Стоит говорить, что нормальной работы при таком раскладе уже не будет (особенно в 2.4ГГц в зашумленном эфире офиса)? TCP, требующий подтверждения доставки, просто встанет колом, голос начнёт квакать и т.д.

Плюс, что бы этого избежать (плюс приземлить побольше клиентов, ведь для этого надо заставить всех работать на самой ближней AP, желательно с максимальными рэйтами и без повторов передачи), админ в сети наверняка на AP настроил handoff с уровнем эдак в -75дБ. Т.е. при -75дБ RSSI handoff со стороны AP застрелит такого клиента (при этом на клиенте уровень от AP будет аж -65дБ, т.е. далеко до заветных -78дБ, когда он решит подумать мигрировать). Т.е. будет link beat, сброс стэйтов и обрыв соединений на пользовательской стороне.

Ещё пример – когда AP видит клиента с уровнем в -75дБ, клиент видит AP с уровнем -65дБ!! Или, когда на клиенте видим -78дБ, то со стороны AP клиент будет слышен лишь с -88дБ уровнем.

Т.е. что бы обеспечить нормальный сервис, мы должны отстреливать клиента сильно раньше, чем он сам подумает мигрировать (имеется в виду, с вышеозначенными умолчаниями в драйвере).

Или же нам придётся снижать мощность на AP что бы “уравнять” шансы, что в условиях грязного эфира может привести к катастрофическому падению SNR и как следствие булькам, хрипам и прочему.

А вот чтобы этого не происходило, достаточно выше обозначенную переменную поправить, выставив значение в диапазоне -65 ~ -70дБ.

Побочный эффект – чуть упадёт скорость у клиента, т.к. чаще будет выполняться фоновые сканирования, плюс незначительно вырастет энергопотребление. Зато у него “будет повод” начать смотреть, кто есть вокруг, и попытаться мигрировать, не дожидаясь, пока ему прилетит по голове от AP.

Следующий блок:

# CCX Support and fast transition
CcxEnabled=0
FastTransitionEnabled=1


CcxEnabled – это поддержка rrm ccx location-measurement, т.е. определения местоположения. Зачастую бесполезна и лишь будет расходовать батарею почём зря.

FastTransitionEnabled – собственно поддержка 802.11R, однако в старых драйверах не полная, но хотя бы умеет учитывать MDIE при миграции (работает всегда, если переменная в значении 1). Заметим, что FT, а именно ускорение фазы аутентификации просто при взводе значения переменной в 1 работать не начнёт, т.к. требуется ещё и Supplicant пропатчить + обвязку андроидную (как это делает, например, Samsung в своих топовых моделях). Однако, учёт MDIE – уже польза. Включаем.

Не забываем также отключить 802.11d,  иначе встретим много чудес в 5ГГц с DFS каналами. Пусть использование или не использование оных лежит на совести админа сети.

# 802.11d support
g11dSupportEnabled=0

Далее блок:

# Legacy (non-CCX, non-802.11r) Fast Roaming Support
# To enable, set FastRoamEnabled=1
# To disable, set FastRoamEnabled=0
FastRoamEnabled=1

Вот оно самое заветное. Включает собственно возможность миграции в любых сетях. Иначе, логика  handover будет активироваться, только если клиент видит, что AP вещает поддержку 802.11R в IECAP. И/или используется WPA*-Enterprise (сюрпрайз)… Ну а как вы хотели? =)

Надо же как-то продавать Enterprise AP, вот и вырубим хэндовер между обычными, не анонсирующими поддержку 802.11R ни в каком виде. Муркетинг животворящий, блин.

Такие клиенты зачастую даже в open сетях висят до последнего. =) Но благо, всё больше вендоров отключают такое поведение установкой вот этой переменной в 1.

В новых драйверах, поставляемых в SDK для Android 6.0.1, добавлена ещё одна прекрасная опция:

# Handoff Enable(1) Disable(0)

gEnableHandoff=0

Эта опция (если выставить в 1) позволяет обрабатывать handoff с пинком со стороны AP как сигнал к миграции, т.е. банально не генерируется LinkBeat при kickout со стороны AP, а сразу выполняется попытка перескочить на следующую подходящую выбранную сканом AP, и только если не получилось сгенерировать Link Beat системе (т.е. о том, что его выпнули, знает только драйвер, и сразу пытается мигрировать, если не получается, то тогда уже сообщает системе, что всё, связи с этой сетью больше нет, надо выбрать другой SSID из тех, которые знает android).

Т.е. пользовательские соединения при этом останутся целыми (ну разве что iperf пострадает, но мессенджеры и голос останутся работать, правда в случае голоса будет квак).

Это слегка нарушает “стандарт” 802.11, но в случае миграции ооочень полезная штука. После её включения, устройство прозрачно (с точки зрения запущенных приложений) сможет мигрировать даже в сетях, где весь роуминг построен исключительно на отстрелах клиента по уровню.

А вот ещё одна крайне полезная крутилка:

#Check if the AP to which we are roaming is better than current AP in terms of RSSI.
#Checking is disabled if set to Zero.Otherwise it will use this value as to how better
#the RSSI of the new/roamable AP should be for roaming
RoamRssiDiff=5

Дельта уровней между AP для выбора кандидата при миграции. Больше значение – меньше вероятность прилететь назад на ту же AP, но более отложенный старт миграции. 5дБ дефолтовых ИМХО маловато. Нужно иметь дельту около 8-10дБ. Для начала выставим 8.

RoamRssiDiff=8

Ещё интересная штука:

#Beacon Early Termination (1 = enable the BET feature, 0 = disable)
enableBeaconEarlyTermination=1
beaconEarlyTerminationWakeInterval=11

Эта опция откидывает все маяки, если в этих фрэймах  не взведён TIM бит, а клиент спит. Т.е. во сне клиент не может вести пассивный сбор данных о соседних AP. Хорошо для батарейки – вероятно, плохо для миграции (надо глубже копать драйвер).

gActiveMaxChannelTime=60
gActiveMinChannelTime=30
gActiveMaxChannelTimeConc=60
gActiveMinChannelTimeConc=30

Настройки времени прослушивания эфира при активном сканировании поканально, в мс. Больше значения – больше времени уйдёт на сканирование всего диапазона. Меньше время – быстрее просканируем, но можем половину не услышать.

RRM включается так:

# 802.11K support
gRrmEnable=1
gRrmOperChanMax=8
gRrmNonOperChanMax=8
gRrmRandIntvl=100

Правда, я на доступных мне железках (в смысле, на тех, в которых было вырублено и включил, на SGS A5 2017 запросы есть, но там и так всё с миграцией более-менее) так и не дождался ни одного запроса с использованием RRM от Yota phone… Видимо, отключена поддержка при сборке драйвера.

Это основное, что касается миграции.

Да и ещё:

# 1: Enable standby, 2: Enable Deep sleep, 3: Enable Mcast/Bcast Filter
gEnableSuspend=3

По дефолту обычно 0. В таком режиме клиент при переходе в режим сна (часто просто при выключении экрана) полностью тушит RF часть, временно просыпаясь только для того, чтобы AP его по таймауту не выпнула. При этом, реализация такова, что и роумингу зачастую становится туго. Следует установить его в 3, т.е. фильтровать броадкасты и мультикасты во сне, и только.

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

Ну и на закуску:

#Channel Bonding
gChannelBondingMode24GHz=1
gChannelBondingMode5GHz=1
gShortGI20Mhz=1
gShortGI40Mhz=1

Поддержка широких каналов (>20МГц) + поддержка SGI. Зачастую для 2.4ГГц поддержка 40МГц отключена, т.е. gChannelBondingMode24GHz установлен в 0. Что, во-первых, не позволяет работать на максимальных рэйтах (150Мбит/с для 1T1R в 2.4ГГц при 40МГц, будет только 72). Увы, проблема в том, что видимо для первого соединения значение перекрывается откуда-то ещё, и даже выставив gChannelBondingMode24GHz=1, сразу мы 150Мбит/с не видит. Но после первой же миграции драйвер плюёт на всех и взлетает в 40МГц полосе. =))

Также установка gChannelBondingMode24GHz решает проблему совместимости с некоторыми 2.4ГГц AP на Xiaomi и One+ (как обычно, намутили с анонсами в зависимости от региона, в итоге AP и клиент не могут договориться, в какой полосе разговаривать).

Не забываем после правки сохранить, перемонтировать назад системный раздел в RO и перезагрузиться.

P.S. Естественно, всё это будет работать только если вендор не отключил поддержку этого счастья на стадии сборки драйвера. Так что тут как повезёт.

Источник