Reflection в Android Pie
Мой канал: VolfsChannel
С каждым годом Android становится всё более закрытой системой. Хотя платформа всё также имеет открытый исходный код, играть не по правилам становится всё труднее. Но у разработчиков есть один полезный инструмент, доставшийся благодаря Java. Его имя - reflection (отражение). Или по просту говоря - прямой доступ даже к приватным методам и классам. Рефлексия позволяет добраться до скрытых API, и извлечь выгоду.
Беда не приходит одна...
Вероятно у вас возникли следующие вопросы:
Так что же всё таки произошло?
Ничего непонятно. Я использую рефлексию, но моё приложение прекрасно работает на Android Pie, где подвох?
Не так быстро, сейчас мы во всём разберемся...
Гугл давно стал беспокоиться о совместимости приложений с различными версиями Android. Были созданы библиотеки android-support, которые приносят более новые API в старые версии Android. Помимо поддержки, библиотеки несут в себе расширение функциональных возможностей приложения. Но одних библиотек мало... Даже вместе с SDK и различными сторонними фреймворками, некоторым разработчикам приходится использовать скрытые возможности Android, которые не заявлены в документации. Это представляет 2 потенциальных угрозы:
- Приложение может навредить функциональности устройства, путем использования скрытых "хуков" ОС
- Приложение может полностью или частично перестать работать в результате изменения алгоритма работы скрытого API в новой версии Android, либо же полного его удаления.
В версии 7.0 Android стал ограничивать использование скрытых интерфейсов NDK с помощью dlopen. Запрещается использование скрытых API библиотек находящиеся в /system/lib/
(что, впрочем, не мешает скопировать и загрузить библиотеку из другого места). В Android 9.0 Гугл ввёл похожие ограничения на использование скрытых API интерфейсов SDK (т.н. non-SDK interfaces
). Так, с этим разобрались. Что же дальше?
Быстрее, выше, сильнее
Само собой, изменение в использовании такого важного компонента не могут пройти в одночасье. Для этого требуется время, и, что не менее важно - альтернативны скрытым API. Android 9 вводит следующую классификацию методов и полей:
- Белый список - все методы и поля публичного SDK [74 000 методов и полей]
- Светло-серый список - поля и методы не являющиеся частью SDK, но тем не менее остающиеся доступными [11 000 методов и полей]
- Тёмно-серый список - для приложений с целевым SDK меньше 28, разрешает использование скрытых методов и полей из этого списка. Для приложений с целевым SDK равным или выше 28 действует аналогично чёрному списку [121 000 методов и полей]
- Чёрный список - ограничен независимо от целевого SDK. Платформа будет вести себят так как будто интерфейс отсутствует. Например, будет бросать NoSuchMethodError/NoSuchFieldException всякий раз, когда приложение пытается использовать его, и не включать его, когда приложение хочет знать список полей/методов определенного класса. [9 000 методов и полей]
Всё это сделано ради нашего же блага, а как же ещё...
Результат использования интерфейсов не из пакета SDK (black, dark-gray lists)
Согласно таблице на официальном сайте, использование скрытых API SDK повлечёт за собой выброс следующих исключений:
Что же делать мне?
Прежде всего, если ваше приложение использует скрытые API, вам необходимо найти замену в лице открытого интерфейса SDK. Если это невозможно, то вам необходимо создать запрос здесь:
https://issuetracker.google.com/issues/new?component=328403&template=1027267 и подробно описать ваш случай использования конкретного поля/метода.
Если вы используете сторонние библиотеки, и не уверены в том, что какая-либо из них не использует запрещённые API, то вы можете проверить ваш DEX с помощью утилиты veridex:
https://android.googlesource.com/platform/prebuilts/runtime/+/master/appcompat
FAQ
Как мне определить, что приложение использует скрытые API?
При использовании скрытых API, в системный журнал будет печататься сообщение следующего вида:
Accessing hidden field|method Lname/of/Class;->nameOfFieldOrMethod: returnType (method list, source-caller (Java/JNI))
Например:
Accessing hidden field Landroid/os/Message;->flags:I (light greylist, JNI)
Где я могу получить список API из черного/серого списка?
Они являются частью платформы Вы можете найти готовые записи здесь:
platform/prebuilts/runtime/appcompat/ hiddenapi-light-greylist.txt
: список светло-серых API
platform/prebuilts/runtime/appcompat/ hiddenapi-dark-greylist.txt
: список темно-серых API
Дополнительно, это список содержит список светло-серых API.
Мы добавили правило, создающее списки на AOSP. Это не то же самое, что и черный список в P, но перекрытие достаточно хорошее. Разработчик может скачать исходный код AOSP, а затем сгенерировать черный список с помощью следующей команды:
make hiddenapi-aosp-blacklist
Файл можно будет просмотреть по пути:
out/target/common/obj/PACKAGING/hiddenapi-aosp-blacklist.txt
Каковы примерные размеры этих списков?
- белый список (также известный как SDK) ~= 74 000 методов и полей
- светло-серый ~= 11 000 методов и полей
- тёмно-серый ~= 121 000 методов и полей
- черный список ~= 9 000 методов и полей
Где я могу найти серый/чёрный список в образе системы?
Они кодируются в поле и битах флага доступа к методу, в файлах платформы dex. В образе системы, содержащем эти списки, нет отдельного файла.
Серый/чёрный список является одинаковым на разных устройствах OEM с одинаковыми версиями Android?
Да, OEM-производители могут добавить свои собственные API в черный список, но не могут удалить методы из оригинального черного/серого списка. CDD предотвращает такие изменения, а CTS тесты гарантируют, что рантайм Android применяет данный список.
Применяются ли ограничения интерфейсов, отличных от SDK, ко всем приложениям, включая системные и сторонние приложения, а не только к сторонним приложениям?
Да, однако, мы освобождаем приложения, подписанные ключом платформы, и у нас также есть белый список пакетов для некоторых приложений с системными ресурсами. Обратите внимание, что эти исключения применимы только к приложениям, которые находятся в образе системы (или обновленных приложениях образа системы). Этот список предназначен только для приложений, которые создаются на основе API-интерфейсов частной платформы, а не в SDK API (т. е. LOCAL_PRIVATE_PLATFORM_APIS := true
).
Как я могу отключить данные ограничения?
Данные ограничения можно отключить на 1 конкретном устройстве с помощью... [Продолжение следует]