Avatar ·

Ошибка android:exported для Activity в Android 12 и выше: как исправить?

Привет, коллеги! Я недавно обновил targetSdk до 31 (Android 12) в своём проекте на Kotlin, и теперь при сборке вылетает ошибка: ``` android:exported needs to be explicitly specified for . Apps targeting Android 12 and higher are required to specify an explicit value for `android:exported` when the corresponding component has an intent-filter defined. ``` Вот мой AndroidManifest.xml: ```xml ``` Я понимаю, что нужно добавить `android:exported`, но не совсем ясно, какое значение ставить. Для MainActivity я поставил `true`, так как это точка входа в приложение. Для SecondActivity — `false`, потому что она запускается только из моего приложения. Но правильно ли это? И что будет, если я забуду указать атрибут для других компонентов (сервисов, ресиверов)? Есть ли какой-то универсальный подход? Буду благодарен за разъяснение с примерами на Kotlin. <<>>

Краткое объяснение проблемы

Начиная с 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 — они подскажут, какие компоненты требуют внимания.