Заставляем Android делать то что нам надо.
Есть у меня довольно простой автомобильный сценарий. Я сажусь в машину, телефон автоматически соединяется по Bluetooth с магнитолой, а самой магнитоле для навигации, пробок и прочих радостей нужен интернет через Wi‑Fi. Каждый раз доставать телефон, открывать шторку и вручную включать точку доступа мне в какой-то момент окончательно надоело.
Казалось бы, задача на пять минут: поймали подключение нужного Bluetooth-устройства и вызвали системную функцию включения hotspot. Но с недавних пор эту лавочку в Android прикрыли. Обычному стороннему приложению прямого доступа к управлению tethering уже не дают, даже если пользователь сам этого хочет.
В итоге получилось небольшое приложение BlueSpot на C#/.NET, которое делает ровно одну вещь: при подключении к выбранному Bluetooth-устройству включает системную Wi‑Fi-точку и присылает уведомление. Root не требуется, но без небольшого легального извращения в виде Shizuku не обошлось.
А почему просто не держать точку включённой всегда?
Такой вопрос возникнет первым, поэтому отвечу сразу: потому что она довольно бодро ест батарею.
Телефон может весь день лежать в кармане, а машина нужна час утром и час вечером. Держать ради этих двух часов постоянно работающий Wi‑Fi hotspot, который всё это время ждёт клиентов, — сомнительная экономия времени. Особенно если забыть поставить телефон на зарядку.
Хотелось ровно такой логики:
Сел в машину → телефон подключился к Bluetooth магнитолы → включилась Wi‑Fi-точка → магнитола получила интернет
Вышел из машины — Android сам отключит неиспользуемую точку по своему тайм-ауту, если эта настройка включена. То есть приложение не пытается заменить весь системный менеджер tethering. Оно только даёт нужный толчок в нужный момент.
Где Android сказал «нельзя»
Писать решил на C# и .NET 10 for Android. Сам Bluetooth-триггер оказался обычной задачей: запросить BLUETOOTH_CONNECT, получить список сопряжённых устройств, сохранить имя и MAC-адрес выбранного и слушать ACTION_ACL_CONNECTED.
А вот на включении hotspot началось интересное.
У Android есть TetheringManager, но возможность управлять tethering завязана на привилегированные разрешения. Простому APK из интернета или магазина системное разрешение уровня TETHER_PRIVILEGED никто не выдаст. Наличие метода в SDK ещё не означает, что его разрешат вызвать вашему приложению.
Сначала я пошёл самым прямолинейным путём и попробовал выполнить системную команду через shell:
cmd connectivity tether start wifi
На моём Xiaomi с Android 16 результат был лаконичный:
Unknown command: tether
Хорошо, команды нет — попробуем вызвать TetheringManager из процесса с правами shell. Вызов уже доходил до системы, но вернулся код 14: нет разрешения изменять состояние tethering. То есть получить ADB shell недостаточно, если Binder-вызов уходит не тем путём или система видит не того вызывающего.
На этом месте можно было либо смириться, либо посмотреть, как задачу уже решают приложения, которые действительно умеют управлять точкой на новых версиях Android.
Что взял за основу
Первым кирпичиком стал Shizuku. Если очень упрощённо, Shizuku запускает на телефоне отдельный процесс с правами ADB shell и даёт приложениям контролируемый доступ к системным Binder API. На Android 11+ его можно запустить через беспроводную отладку, без root и без постоянного подключения к компьютеру.
Важно: код Shizuku нельзя просто «встроить в наше приложение», чтобы пользователь ничего не заметил. Для первого запуска повышенного процесса Android всё равно требует доверенную точку входа — ADB, беспроводную отладку или root. Обычный APK не может сам выдать себе права shell, иначе вся модель безопасности Android потеряла бы смысл.
Вторым и главным ориентиром стал проект Delta — приложение для расширенного управления hotspot. Delta уже работала на том же телефоне, а значит, вместо гадания можно было разобрать рабочий путь.
В Delta интересовали две части:
получение системного сервиса
tetheringи оборачивание его Binder черезShizukuBinderWrapper;вызов скрытого
ITetheringConnector.startTetheringс системнымTetheringRequest.
Ещё понадобились HiddenApiBypass, потому что Android ограничивает обращения обычных приложений к скрытым API, и Refine, чтобы скомпилировать небольшой Java-мост против системных stubs, не запаковав поддельные классы android.* внутрь APK.
Сам рабочий маршрут в итоге выглядит так:
BlueSpot (.NET/C#) → минимальный Java AAR-мост → SystemServiceHelper.getSystemService("tethering") → ShizukuBinderWrapper → ITetheringConnector.Stub.asInterface(...) → startTethering(TETHERING_WIFI) → системный сервис Android
Это важное отличие от первой попытки. BlueSpot не просит Shizuku выполнить случайную консольную строку. Он передаёт через Shizuku конкретную Binder-транзакцию системному tethering-сервису тем же способом, который оказался рабочим в Delta.
Минимальная суть Java-моста, без обработки разных сигнатур Android, выглядит примерно так:
IBinder raw = SystemServiceHelper.getSystemService("tethering"); ITetheringConnector connector = ITetheringConnector.Stub.asInterface( new ShizukuBinderWrapper(raw) ); TetheringRequest request = new TetheringRequest.Builder(TETHERING_WIFI).build(); connector.startTethering( request.getParcel(), "com.android.shell", null, listener );
В реальном коде есть запасные варианты сигнатуры для старых Android, ожидание callback с кодом результата и нормальная диагностика ошибок. Сам мост лежит в репозитории рядом с исходниками приложения. Код адаптирован из Delta с сохранением уведомления BSD-3-Clause.
Как устроен сам триггер
BlueSpot хранит только выбранные пользователем имя и MAC-адрес Bluetooth-устройства плюс состояние переключателя автоматизации.
После включения автоматизации запускается foreground service. Да, это означает постоянное малозаметное уведомление. На современных Android именно так и должен честно работать долгоживущий фоновый процесс, который пользователь может в любой момент увидеть и остановить.
Сервис использует сразу два способа определения подключения:
Слушает системные события
ACTION_ACL_CONNECTEDиACTION_ACL_DISCONNECTED.Раз в восемь секунд проверяет фактическое состояние выбранного устройства.
Второй способ нужен не от хорошей жизни. Производители телефонов любят экономить батарею весьма творчески, события могут потеряться после перезапуска процесса, а сервис может стартовать, когда магнитола уже подключена. Периодическая проверка страхует эти случаи.
При этом триггер срабатывает только при точном совпадении сохранённого MAC-адреса. Просто любое Bluetooth-подключение — часы, наушники или случайная колонка — точку не запустит.
Если Shizuku после перезагрузки ещё не запущен, BlueSpot покажет понятное уведомление и будет повторять попытку раз в минуту, пока магнитола остаётся подключённой. Это удобнее, чем требовать угадать идеальный порядок запуска приложений утром.
После успешного Binder-callback появляется отдельное уведомление: точка доступа запущена. Имя сети и пароль BlueSpot не придумывает и не хранит — используются уже сохранённые системные настройки телефона.
Почему Shizuku приходится включать после перезагрузки
Здесь есть неизбежный компромисс. При полной перезагрузке Android убивает процесс Shizuku, а обычное приложение не имеет права самостоятельно снова поднять процесс с ADB-привилегиями.
Сопряжение беспроводной отладки обычно выполняется один раз, но кнопку запуска Shizuku после перезагрузки нажать придётся снова. Это не недоделка BlueSpot и не лень разработчика, а граница безопасности Android. Официальная инструкция Shizuku говорит о том же.
С root такой проблемы нет, но цель как раз была сделать решение для обычного телефона без разблокировки загрузчика. Для моего сценария один запуск Shizuku после редкой перезагрузки оказался приемлемой ценой за ежедневную автоматизацию.
Настройка для пользователя
В приложении оставлены только необходимые настройки, без тестовых кнопок и полотна диагностического лога:
Разрешить Bluetooth и уведомления.
Установить и запустить Shizuku через беспроводную отладку.
Выдать BlueSpot разрешение внутри Shizuku.
Выбрать сопряжённую магнитолу.
Один раз настроить имя и пароль системной точки доступа.
Разрешить приложению автозапуск и фоновую работу без ограничений.
Включить автоматизацию.
Нужные пункты настроек открываются прямо из BlueSpot. Для HyperOS добавлены переходы в автозапуск и управление батареей, потому что иначе прошивка вполне может решить, что знает желания пользователя лучше самого пользователя.
После этого сценарий выглядит именно так, как и задумывалось: сел в машину, Bluetooth соединился, Wi‑Fi появился, магнитола подключилась.
Безопасность и ограничения
Shizuku даёт приложению заметно больше возможностей, чем обычный Android sandbox, поэтому ставить APK непонятного происхождения и раздавать ему Shizuku-разрешения точно не стоит.
В BlueSpot нет разрешения на интернет. Приложение не отправляет наружу список Bluetooth-устройств, MAC-адрес или какие-либо логи. Исходники открыты, готовый APK публикуется в Releases, а SHA-256 для версии 1.0.0 указан в описании релиза.
При этом я не обещаю универсальность на каждой прошивке. Samsung, Xiaomi, Google и остальные могут по-разному менять tethering-сервис, ограничения фоновой работы и системные сигнатуры. Текущая версия проверена на Xiaomi 14 Ultra, HyperOS и Android 16. Обновление системы теоретически может снова прикрыть и эту дорожку.
Немного про разработку с нейросетью
Программа сделана с использованием сторонних open-source-проектов и нейросети. Нейросеть помогала собирать C#-часть, сравнивать реализацию с Delta, разбирать логи реального телефона и приводить интерфейс в приличный вид.
Результат
Получилось маленькое приложение для очень конкретной бытовой проблемы. Можно было каждый раз продолжать нажимать кнопку в шторке. Можно было постоянно держать hotspot и заряжать телефон чаще. Но где тогда удовольствие?
В целях экономии батареи — такое вот лёгкое извращение.
Репозиторий и исходники: github.com/nikostap/BlueSpot
Готовый APK: последний релиз BlueSpot
Shizuku: официальная загрузка
Инструкция по запуску Shizuku: shizuku.rikka.app/guide/setup
Если кто-то проверит на других производителях и версиях Android — результаты и логи приветствуются в Issues.
Lev3250
Эээ, как то я не проникся темой. Почему просто не сделать вот так?
Samsung s24fe
nikostap Автор
Потому что на последних адройд версиях это залочено для управления и без рута доступа нн получить, а напртмер на Honor вообще сейчас рут не получить.
Lev3250
А родной программы для этого нет? Я не пользовался Xiaomi уже пару поколений, но вроде что-то подобное там было, не?