Ошибка android:exported для Activity в Android 12 и выше: как исправить?
Краткое объяснение проблемы
Начиная с Android 12 (API 31), Google ужесточил требования безопасности. Теперь каждый компонент вашего приложения (Activity, Service, BroadcastReceiver, ContentProvider), у которого есть <intent-filter>, обязан явно указать атрибут android:exported. Это сделано, чтобы предотвратить случайный экспорт компонентов, которые могут быть вызваны извне (другими приложениями или системой).
Как правильно задать значение
android:exported="true"— если компонент может быть запущен из другого приложения или системой. Обычно это:- Launcher Activity (точка входа, как в вашем случае)
- Activity, которая обрабатывает
ACTION_VIEWилиACTION_SEND(например, для открытия ссылок) - BroadcastReceiver, который слушает системные события (например,
BOOT_COMPLETED)
android:exported="false"— если компонент используется только внутри вашего приложения. Это стандартный случай для:- Внутренних Activity
- Сервисов, которые запускаются только из вашего кода
- BroadcastReceiver, которые регистрируются динамически
Исправленный код для вашего примера
Вот как должен выглядеть ваш AndroidManifest.xml:
<activity
android:name=".MainActivity"
android:exported="true" <!-- Launcher — доступно извне -->
android:label="@string/app_name">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity
android:name=".SecondActivity"
android:exported="false" /> <!-- Только для внутреннего использования -->
Важное замечание для компонентов без intent-filter
Если у компонента нет <intent-filter>, то android:exported можно не указывать — он автоматически считается false. Но если вы всё же укажете, ошибки не будет. Рекомендую всегда явно прописывать атрибут для единообразия.
Как это проверить в коде Kotlin
Если вы динамически проверяете, может ли ваше приложение обработать Intent, используйте PackageManager:
val intent = Intent(Intent.ACTION_VIEW).apply {
data = Uri.parse("https://example.com")
}
val resolveInfo = packageManager.resolveActivity(intent, PackageManager.MATCH_DEFAULT_ONLY)
if (resolveInfo != null) {
// Activity доступна
} else {
// Нет подходящего компонента
}
Что делать с другими компонентами
Проверьте все <service>, <receiver> и <provider> в вашем манифесте. Если у них есть <intent-filter>, добавьте android:exported. Например, для BroadcastReceiver:
<receiver
android:name=".MyReceiver"
android:exported="false">
<intent-filter>
<action android:name="com.example.MY_CUSTOM_ACTION" />
</intent-filter>
</receiver>
Если вы используете сторонние библиотеки (например, Firebase или WorkManager), проверьте их манифест — иногда они уже содержат правильные атрибуты, но могут конфликтовать. В таком случае добавьте tools:replace="android:exported" в свой манифест.
Итог
Главное правило: если компонент должен быть доступен извне — true, если только внутри приложения — false. Не ленитесь — пропишите атрибут для всех компонентов с <intent-filter>. Это убережёт вас от ошибок сборки и возможных уязвимостей.
Альтернативный подход: использование инструментов сборки
Если у вас большой проект и вы не хотите вручную править каждый компонент, можно автоматизировать процесс с помощью Gradle. Например, используйте плагин AndroidManifest Merger или напишите простую задачу в build.gradle.kts:
android {
defaultConfig {
// Автоматически добавляем exported для всех activity
manifestPlaceholders["exportedForAllActivities"] = "false"
}
}
Затем в манифесте:
<activity
android:name=".SomeActivity"
android:exported="${exportedForAllActivities}" />
Но это костыль. Лучше всё-таки явно указать значения, так как автоматизация может дать ложное чувство безопасности.
Дополнительный совет: тестирование
После добавления атрибутов обязательно протестируйте приложение на Android 12+. Попробуйте запустить Activity из других приложений (например, через adb):
adb shell am start -n com.example.app/.SomeActivity
Если получили Permission Denial, а ожидали, что Activity доступна — проверьте android:exported. Если же Activity не должна быть доступна, а запуск удался — срочно меняйте на false.
Также проверьте логи на предмет ExportWarning — они подскажут, какие компоненты требуют внимания.