В Android 12 появится важное изменение, улучшающее безопасность платформы целиком и всех приложений, предназначенных для работы с этой версией ОС. Активити, сервисы и бродкаст ресиверы (broadcast receivers), в которых указаны интент-фильтры (intent-filters), должны явно обозначать, будут ли они доступны для других приложений или компонентов системы.

Далее перевод статьи про проблемы и решения, которые могут возникнуть при переходе на новую версию.

Ошибки

Если установка вашего приложения завершается одним из сообщений ниже, скорее всего, это связано с новыми изменениями.

Installation did not succeed.
The application could not be installed: INSTALL_FAILED_VERIFICATION_FAILURE
List of apks:
[0] ‘…buildoutputsapkdebugapp-debug.apk’
Installation failed due to: ‘null’

Или

INSTALL_PARSE_FAILED_MANIFEST_MALFORMED:
Failed parse during installPackageLI: /data/app/vmdl538800143.tmp/base.apk (at Binary XML file line):
com.example.package.ActivityName: Targeting S+ (version 10000 and above) requires that
an explicit value for android:exported be defined when intent filters are present”

Решение

Обе эти ошибки можно устранить, добавив атрибут `android:exported` к каждому компоненту манифеста приложения, таким как <activity>, <activity-alias>, <service> или <receiver>, если в них объявлены свои интент-фильтры.

Но не следует бездумно добавлять `android:exported=”true”` ко всем этим элементам. Просмотрите каждый компонент, включающий в себя <intent-filter> и спросите себя: «Хочу ли я, чтобы любое приложение, установленное на устройстве, могло запустить этот компонент?».

Ответ на этот вопрос зависит от назначения приложения, взаимодействия с ним другого ПО и иных, более специфических условий. Ниже представлены несколько распространённых примеров интент-фильтров с рекомендуемым значением exported-атрибута и объяснением выбора.

  • Активити с <category android:name=”android.intent.category.LAUNCHER” /> : android:exported=”true”

    Скорее всего, это MainActivity вашего приложения и поскольку лаунчер в Android может быть обычным приложением, атрибут exported необходим, иначе лаунчер не сможет запустить её.

  • Активити с <action android:name=”android.intent.action.VIEW” /> : android:exported=”true”

    Эта активити отвечает за обработку действия «открыть с помощью» из других приложений.

  • Активити с <action android:name=”android.intent.action.SEND” /> или <action android:name=”android.intent.action.SEND_MULTIPLE”/>: android:exported=”true”

    Позволяет обрабатывать контент, которым могут делиться другие приложения.

  • Сервис с <action android:name=”android.media.browse.MediaBrowserService” /> : android:exported=”true”

    Если сервис предоставляет другим приложениям доступ к медиа-библиотеке вашего, то он должен быть экспортированным (exported), чтобы разрешить приложениям использовать эту библиотеку. Скорее всего, такой сервис, напрямую или опосредованно, наследуется от MediaBrowserServiceCompat. Если это не так, то возможно, атрибут exported необязателен.

  • Сервис с <action android:name=”com.google.firebase.MESSAGING_EVENT” /> : android:exported=”false”

    Это сервис Firebase Cloud Messaging, он должен наследоваться от FirebaseMessagingService и не должен быть экспортированным, поскольку Firebase может запустить компонент вне зависимости от факта его экспортации. Подробности можно узнать по ссылке: Set up a Firebase Cloud Messaging client app on Android

  • Ресивер с <action android:name=”android.intent.action.BOOT_COMPLETED” /> :*android:exported=”false”

    Система может доставлять это событие в бродкаст ресивер вне зависимости от факта экспортации.

Бэкграунд

Вплоть до Android 12, активити, сервисы и бродкаст ресиверы с обозначенным интент-фильтром автоматически становились экспортированными.

Эта активити экспортирована по умолчанию:

<activity android:name=“.MainActivity”>
   <intent-filter>
       <action android:name=“android.intent.action.MAIN” />
       <category android:name=“android.intent.category.LAUNCHER”
   </intent-filter>
</activity>

А эта — нет:

<activity android:name=“.MainActivity” />

И хотя подобное дефолтное поведение выглядит разумным, ошибка может сделать ваше приложение уязвимым. Например, приложение имеет активити для проигрывания видео: 

<activity android:name=“.PlayVideoActivity” />

Позже обнаруживается, что мы обращаемся к этой активити из множества разных мест в приложении, и, чтобы уменьшить связность, меняем её, включая интент-фильтр для выбора активити системой:

<activity android:name=”.PlayVideoActivity”>
   <intent-filter>
       <action android:name=”android.intent.action.VIEW” />
       <data
           android:mimeType=”video/*”
           android:scheme=”content” />
   </intent-filter>
</activity>

Внезапно появляется проблема. Наша активити, созданная исключительно для использования внутри приложения, внезапно становится экспортированной!

Но при таргете на Android 12 система предотвратит подобное поведение, требуя от нас явно указать значение атрибута android:exported. И поскольку в данном случае мы не хотим, чтобы активити была экспортирована — ставим атрибут android:exported = false, чтобы обезопасить приложение.

<activity
   android:name=”.PlayVideoActivity”
   android:exported=”false”>
   <intent-filter>
       <action android:name=”android.intent.action.VIEW” />
       <data
           android:mimeType=”video/*”
           android:scheme=”content” />
   </intent-filter>
</activity>

TL;DR

Для улучшения безопасности в Android 12 появится важное изменение. Приложения, у которых эта версия указана в target SDK, должны будут явно объявлять значение атрибута `android:exported` для любых активити, сервисов или ресиверов, которые используют интент-фильтры в AndroidManifest.xml. Без него приложение просто не установится.

Тщательно принимайте решение касательно значения этого атрибута и, если сомневаетесь — предпочитайте android:exported=”false”.

Для получения более подробной информации об интентах и интент-фильтрах смотрите Receiving an implicit intent.

О других изменениях безопасности и приватности читайте на этой странице.