Что значит флаг exported у сервиса в Android и на что он влияет
Что проверяют
Понимаете ли вы, где проходит граница приложения, и что манифест это публичный список точек входа, доступных другим приложениям на устройстве. Вопрос почти всегда уводят в безопасность: за ним идёт «а как защитить сервис, если он всё-таки нужен снаружи».
Как отвечать
Атрибут android:exported у <service> определяет, может ли компонент быть запущен из другого приложения, то есть из другого UID.
Значение по умолчанию зависит от наличия <intent-filter>: если фильтр объявлен, по умолчанию компонент экспортирован, если нет, то закрыт. Полагаться на это умолчание нельзя: начиная с API 31 для компонента с intent-filter атрибут нужно указать явно, иначе приложение просто не соберётся и не установится.
<service
android:name=".SyncService"
android:exported="false" />Если сервис действительно нужен внешним приложениям, его закрывают разрешением: android:permission с уровнем signature пускает только приложения, подписанные тем же ключом, а данные из входящего интента проверяют как недоверенные. Полезно добавить, что неявные интенты к сервисам запрещены давно: bindService и запуск требуют явного указания компонента.
Где спотыкаются
Считают, что exported="false" это полная изоляция. Компоненты своего приложения и приложения с тем же UID достучатся, отладочный доступ через adb тоже остаётся, а если рядом есть экспортированный компонент, который просто пробрасывает вызов дальше, закрытость первого ничего не даёт.
Ставят exported="true", чтобы «заработал запуск», и не добавляют разрешение. Тогда любое установленное приложение шлёт интент и дёргает вашу логику. Классическая дыра: сервис принимает из интента путь к файлу или идентификатор аккаунта и работает с ним без проверки вызывающего.
Путают экспорт с фильтром намерений. Убрать <intent-filter> недостаточно, если атрибут остался истинным: компонент по-прежнему запускается по явному имени класса. И наоборот, фильтр сам по себе не открывает доступ, когда экспорт выключен явно.