02.08.2026 129 материалов

Как блокировать приложения на Android без AccessibilityService — альтернативный подход

Практически все блокировщики приложений в Google Play используют AccessibilityService, но это избыточный и подозрительный для пользователя инструмент. Разработчик приложения Takeover показал, как обойтись более щадящим подходом — и объяснил, где тут подводные камни.

Как блокировать приложения на Android без AccessibilityService — альтернативный подход

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

Зачем вообще отказываться от AccessibilityService

AccessibilityService — мощный инструмент Android, задуманный для помощи людям с ограниченными возможностями: экранные дикторы, альтернативные способы ввода, автоматизация. Разработчики блокировщики давно приспособили его для своих нужд: сервис получает колбэк при каждой смене окна, читает имя пакета и при необходимости перекрывает запрещённое приложение. Просто, эффективно, работает.

Проблема в том, что при установке такого приложения пользователь видит пугающий запрос: «Это приложение может видеть всё, что отображается на экране». В том числе — содержимое банковского приложения, переписок, паролей. С точки зрения Google, если задачу можно решить более узким API, нужно использовать именно его. Ревьюеры в Play Store действительно отклоняют блокировщики, запрашивающие AccessibilityService без крайней необходимости, а пользователи всё чаще отказываются давать это разрешение.

Альтернатива: UsageStatsManager + foreground service

Разработчик приложения Takeover (фокус-блокировщик) описывает подход, который он запустил в продакшене. Суть проста:

  1. Foreground service каждые ~900 мс опрашивает UsageStatsManager, чтобы узнать, какое приложение сейчас на переднем плане.
  2. Если это приложение из «чёрного списка», поверх него запускается полупрозрачная Activity — экран блокировки.

Всего три разрешения в манифесте:

  • PACKAGE_USAGE_STATS — доступ к статистике использования
  • SYSTEM_ALERT_WINDOW — отображение окна поверх других приложений
  • FOREGROUND_SERVICE + FOREGROUND_SERVICE_SPECIAL_USE — работа в фоне

Ключевой нюанс: PACKAGE_USAGE_STATS — не runtime-разрешение. Его нельзя запросить стандартным диалогом. Пользователь должен сам зайти в Настройки → Специальный доступ → Доступ к статистике использования и включить его. Разработчик честно признаёт: это серьёзный момент оттока при онбординге. Экран выглядит пугающе, расположен в нетривиальном месте настроек, и никакой текст его не сделает привычным.

Как правильно читать активное приложение

Основной цикл — foreground service с Handler, который вызывает проверку каждые 900 мс. Метод checkForeground() через queryEvents() получает события типа MOVE_TO_FOREGROUND и запоминает имя пакета.

Здесь автор выделяет две типичные ошибки, которые не найти в туториалах:

1. queryEvents возвращает события, а не состояние. Если пользователь сидит в одном приложении минуту, запрос вернёт пустоту — ведь ничего не изменилось. Это главный баг во всех подобных реализациях: разработчик сбрасывает состояние в «нет активного приложения», и блокировка мигает или пропадает. Решение — хранить последнее известное приложение в поле и обновлять только при реальном событии.

2. Используйте скользящее окно, а не фиксированное. Формат [lastQueryTime, now], где lastQueryTime сдвигается только после успешного чтения. Буфер событий не строго реалтаймный — некоторые события приходят с задержкой в сотни миллисекунд. Фиксированное окно [now - 1000, now] их потеряет. При старте сервиса нужно отступить на пару секунд назад, иначе пропустите переход, случившийся прямо перед началом мониторинга.

Экран блокировки

Обычная Activity, но с тремя настройками в манифесте:

  • Полупрозрачная тема (Theme.Translucent.NoTitleBar.Fullscreen)
  • excludeFromRecents="true" — не появляется в списке недавних приложений
  • launchMode="singleInstance" — одна копия, не стакуется

Флаги запуска: FLAG_ACTIVITY_NEW_TASK or FLAG_ACTIVITY_CLEAR_TOP or FLAG_ACTIVITY_SINGLE_TOP. Комбинация singleInstance + SINGLE_TOP предотвращает ситуацию, когда при открытии заблокированного приложения на экране «стробоскопом» мелькают шесть копий блокировки.

Три защитных механизма, без которых ничего не работает

Автор особо подчёркивает: ни один туториал этого не упоминает, но именно эти проверки отличают «работает на эмуляторе» от «работает на чужом телефоне».

Повторные события при запуске. Некоторые приложения генерируют несколько событий MOVE_TO_FOREGROUND при открытии. Без защиты блокировка запускается, потом приходит следующее событие, потом ещё — пользователь видит мигание. Решение: флаг @Volatile на Activity, показывающий, что блокировка уже видна, и пропуск повторного запуска для того же пакета в течение секунды.

Возврат в своё приложение — тоже переход. Когда пользователь нажимает «назад», заблокированное приложение на мгновение всплывает. Нужен маркер перехода с игнорированием этого пакета на ~3 секунды.

Реальный способ обойти блокировку. Если пользователь намеренно разблокировал приложение, нужно дать ему «льготный период» — 5 минут без блокировки для этого конкретного приложения. Блокировщик без лазейки не воспитывает дисциплину — его просто удаляют.

Тип foreground-сервиса на Android 14+

Начиная с Android 14 нужно явно указать тип foreground-сервиса. Блокировка приложений не попадает ни под один специфический тип, поэтому используется specialUse. В манифесте нужно добавить property с объяснением для ревьюера Google Play: мол, сервис обеспечивает блокировку приложений, выбранную пользователем, путём определения активного приложения через usage access.

Запуск — через startForegroundService, а в startForeground на API 34+ передаётся FOREGROUND_SERVICE_TYPE_SPECIAL_USE. Возвращаемое значение START_STICKY — чтобы система перезапустила мониторинг, если убьёт процесс во время сессии.

Цена подхода: что вы теряете

Автор честно перечисляет компромиссы:

  • Задержка. Опрос раз в 900 мс означает до секунды между моментом, когда пользователь открыл запрещённое приложение, и появлением блокировки. AccessibilityService срабатывает практически мгновенно. Для реального кейса это приемлемо — пользователь на секунду увидит ленту, а потом её не увидит.

  • Нетипичное разрешение. Пользователь должен сам найти нужный экран в настройках и включить переключатель. Никакого диалога с одной кнопкой.

  • Постоянное уведомление. Foreground service = значок в шторке. Без вариантов.

  • Производители ломают. Агрессивная экономия батареи на некоторых оболочках (MIUI, One UI, EMUI) убивает сервис. START_STICKY помогает, но не гарантирует.

Зато вы никогда не просили разрешения читать экран пользователя

Для блокировщика это правильный компромисс — и с точки зрения прохождения ревью в Play Store, и с точки зрения доверия пользователя.

Важная оговорка

Это не инструмент родительского контроля «военного уровня». Любой может зайти в Настройки и отозвать доступ к статистике — и блокировки как не бывало. Цель дизайна — не сделать обход невозможным, а сделать его усилием на ~30 секунд осознанных действий. Как показывает практика, этого обычно хватает: желание открыть Instagram длится меньше, чем путь до настроек.