September 10, 2018

Reflection в Android Pie

Мой канал: VolfsChannel

С каждым годом Android становится всё более закрытой системой. Хотя платформа всё также имеет открытый исходный код, играть не по правилам становится всё труднее. Но у разработчиков есть один полезный инструмент, доставшийся благодаря Java. Его имя - reflection (отражение). Или по просту говоря - прямой доступ даже к приватным методам и классам. Рефлексия позволяет добраться до скрытых API, и извлечь выгоду.


Беда не приходит одна...

Вероятно у вас возникли следующие вопросы:

Так что же всё таки произошло?
Ничего непонятно. Я использую рефлексию, но моё приложение прекрасно работает на Android Pie, где подвох?

Не так быстро, сейчас мы во всём разберемся...

Гугл давно стал беспокоиться о совместимости приложений с различными версиями Android. Были созданы библиотеки android-support, которые приносят более новые API в старые версии Android. Помимо поддержки, библиотеки несут в себе расширение функциональных возможностей приложения. Но одних библиотек мало... Даже вместе с SDK и различными сторонними фреймворками, некоторым разработчикам приходится использовать скрытые возможности Android, которые не заявлены в документации. Это представляет 2 потенциальных угрозы:

  1. Приложение может навредить функциональности устройства, путем использования скрытых "хуков" ОС
  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 конкретном устройстве с помощью... [Продолжение следует]

@VolfsChannel