Наложения ресурсов времени выполнения (RRO)

Наложение ресурсов времени выполнения (RRO) — это пакет, который изменяет значения ресурсов целевого пакета во время выполнения. Например, приложение, установленное в образе системы, может изменить свое поведение в зависимости от значения ресурса. Вместо того, чтобы жестко задавать значение ресурса во время сборки, RRO, установленный в другом разделе, может изменять значения ресурсов приложения во время выполнения.

RRO могут быть включены или отключены. Вы можете программно установить состояние включения/выключения, чтобы переключить способность RRO изменять значения ресурсов. RRO по умолчанию отключены (однако статические RRO по умолчанию включены).

Наложение ресурсов

Наложения работают путем сопоставления ресурсов, определенных в пакете наложения, с ресурсами, определенными в целевом пакете. Когда приложение пытается разрешить значение ресурса в целевом пакете, вместо этого возвращается значение ресурса наложения, с которым сопоставлен целевой ресурс.

Настройка манифеста

Пакет считается пакетом RRO, если он содержит <overlay> в качестве дочернего элемента <manifest> .

  • Значение обязательного атрибута android:targetPackage указывает имя пакета, который RRO намеревается наложить.

  • Значение необязательного атрибута android:targetName указывает имя накладываемого подмножества ресурсов целевого пакета, который RRO намеревается наложить. Если цель не определяет перекрывающийся набор ресурсов, этот атрибут не должен присутствовать.

В следующем коде показан пример наложения AndroidManifest.xml .

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.overlay">
    <application android:hasCode="false" />
    <overlay android:targetPackage="com.example.target"
                   android:targetName="OverlayableResources"/>
</manifest>

Оверлеи не могут накладывать код, поэтому они не могут иметь файлы DEX. Кроме того, для атрибута android:hasCode <application > в манифесте должно быть установлено значение false .

Определение карты ресурсов

В Android 11 или более поздней версии рекомендуемый механизм определения карты ресурсов наложения заключается в создании файла в каталоге res/xml пакета наложения, перечислении целевых ресурсов, которые должны быть наложены, и их замещающих значений, а затем установке значения Атрибут android:resourcesMap тега манифеста <overlay> для ссылки на файл сопоставления ресурсов.

В следующем коде показан пример файла res/xml/overlays.xml .

<?xml version="1.0" encoding="utf-8"?>
<overlay xmlns:android="http://schemas.android.com/apk/res/android" >
    <!-- Overlays string/config1 and string/config2 with the same resource. -->
    <item target="string/config1" value="@string/overlay1" />
    <item target="string/config2" value="@string/overlay1" />

    <!-- Overlays string/config3 with the string "yes". -->
    <item target="string/config3" value="@android:string/yes" />

    <!-- Overlays string/config4 with the string "Hardcoded string". -->
    <item target="string/config4" value="Hardcoded string" />

    <!-- Overlays integer/config5 with the integer "42". -->
    <item target="integer/config5" value="42" />
</overlay>

В следующем коде показан пример манифеста наложения.

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.overlay">
    <application android:hasCode="false" />
    <overlay android:targetPackage="com.example.target"
                   android:targetName="OverlayableResources"
                   android:resourcesMap="@xml/overlays"/>
</manifest>

Сборка пакета

Android 11 или более поздняя версия поддерживает правило сборки Soong для наложений, которое предотвращает попытки Android Asset Packaging Tool 2 (AAPT2) дедупликации конфигураций ресурсов с тем же значением ( --no-resource-deduping ) и удаление ресурсов без конфигураций по умолчанию ( --no-resource-removal ). В следующем коде показан пример файла Android.bp .

runtime_resource_overlay {
    name: "ExampleOverlay",
    sdk_version: "current",
}

Разрешение ресурсов

Если целевой ресурс или ресурс наложения имеет несколько конфигураций, определенных для запрашиваемого ресурса, среда выполнения ресурсов возвращает значение конфигурации, которое лучше всего соответствует конфигурации конфигурации устройства. Чтобы определить, какая конфигурация является наиболее подходящей конфигурацией, объедините набор конфигураций ресурсов наложения с набором конфигураций целевых ресурсов, а затем следуйте процедуре разрешения обычных ресурсов (подробности см. в разделе Как Android находит наиболее подходящий ресурс ).

Например, если оверлей определяет значение для конфигурации drawable-en , а цель определяет значение для drawable-en-port , drawable-en-port имеет лучшее соответствие, поэтому значение целевой конфигурации drawable-en-port равно выбирается во время выполнения. Чтобы наложить все конфигурации drawable-en , наложение должно определить значение для каждой конфигурации drawable-en , которую определяет цель.

Оверлеи могут ссылаться на свои собственные ресурсы с разным поведением в разных версиях Android.

  • В Android 11 или более поздней версии каждое наложение имеет собственное зарезервированное пространство идентификатора ресурса, которое не перекрывает пространство идентификатора целевого ресурса или другое пространство идентификатора ресурса наложения, поэтому наложения, ссылающиеся на собственные ресурсы, работают должным образом.

  • В Android 10 или более ранней версии оверлеи и целевые пакеты используют одно и то же пространство идентификаторов ресурсов, что может привести к конфликтам и неожиданному поведению, когда они пытаются ссылаться на свои собственные ресурсы с использованием синтаксиса @type/name .

Включение/выключение наложений

Используйте API OverlayManager для включения и отключения изменяемых наложений (получите интерфейс API с помощью Context#getSystemService(Context.OVERLAY_SERVICE) ). Оверлей может быть включен только пакетом, на который он нацелен, или пакетом с разрешением android.permission.CHANGE_OVERLAY_PACKAGES . Когда наложение включено или отключено, события изменения конфигурации распространяются на целевой пакет, и целевые действия перезапускаются.

Ограничение накладываемых ресурсов

В Android 10 или более поздней версии XML-тег <overlayable> предоставляет набор ресурсов, которые RRO могут накладывать друг на друга. В следующем примере файла res/values/overlayable.xml string/foo и integer/bar являются ресурсами, используемыми для оформления внешнего вида устройства; для наложения этих ресурсов наложение должно явно указывать набор накладываемых ресурсов по имени.

<!-- The collection of resources for theming the appearance of the device -->
<overlayable name="ThemeResources">
       <policy type="public">
               <item type="string" name="foo/" />
               <item type="integer" name="bar/" />
       </policy>
       ...
</overlayable>

APK может определять несколько тегов <overlayable> , но каждый тег должен иметь уникальное имя в пакете. Например, это:

  • Хорошо, если два разных пакета определяют <overlayable name="foo"> .

  • Недопустимо, чтобы в одном APK было два <overlayable name="foo"> .

В следующем коде показан пример наложения в файле AndroidManifest.xml .

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
       package="com.my.theme.overlay">
       <application android:hasCode="false" />
       <!-- This overlay will override the ThemeResources resources -->
       <overlay android:targetPackage="android" android:targetName="ThemeResources">
</manifest>

Когда приложение определяет <overlayable> , наложения, ориентированные на это приложение:

  • Необходимо указать targetName .

  • Может накладывать только ресурсы, перечисленные в <overlayable> .

  • Можно настроить таргетинг только на одно имя <overlayable> .

Вы не можете включить наложение для пакета, который предоставляет ресурсы для наложения, но не использует android:targetName для нацеливания на определенный <overlayable> .

Политики ограничения

Используйте <policy> , чтобы установить ограничения на накладываемые ресурсы. Атрибут type указывает, каким политикам должно соответствовать наложение, чтобы переопределить включенные ресурсы. Поддерживаемые типы включают следующие.

  • public . Любое наложение может переопределить ресурс.
  • system . Любое наложение на системный раздел может переопределить ресурсы.
  • vendor . Любое наложение на раздел поставщика может переопределить ресурсы.
  • product . Любое наложение на раздел продукта может переопределить ресурсы.
  • signature . Любое наложение, подписанное той же подписью, что и целевой APK, может переопределить ресурсы.

В следующем коде показан пример <policy> в файле res/values/overlayable.xml .

<overlayable name="ThemeResources">
   <policy type="vendor" >
       <item type="string" name="foo" />
   </policy>
   <policy type="product|signature"  >
       <item type="string" name="bar" />
       <item type="string" name="baz" />
   </policy>
</overlayable>

Чтобы указать несколько политик, используйте вертикальные черты (|) в качестве символов-разделителей. Когда указано несколько политик, оверлею необходимо выполнить только одну политику, чтобы переопределить ресурсы, перечисленные в <policy> .

Настройка наложений

Android поддерживает различные механизмы настройки изменчивости, состояния по умолчанию и приоритета наложений в зависимости от версии выпуска Android.

  • Устройства под управлением Android 11 или более поздней версии могут использовать файл OverlayConfig ( config.xml ) вместо атрибутов манифеста. Использование файла наложения является рекомендуемым методом для наложений.

  • Все устройства могут использовать атрибуты манифеста ( android:isStatic и android:priority ) для настройки статических RRO.

Использование оверлейконфига

В Android 11 или более поздней версии вы можете использовать OverlayConfig для настройки изменчивости, состояния по умолчанию и приоритета наложений. Чтобы настроить наложение, создайте или измените файл, расположенный по адресу partition/overlay/config/config.xml , где partition — это раздел настраиваемого наложения. Для настройки оверлей должен находиться в каталоге overlay/ раздела, в котором он настроен. В следующем коде показан пример product/overlay/config/config.xml .

<config>
    <merge path="OEM-common-rros-config.xml" />
    <overlay package="com.oem.overlay.device" mutable="false" enabled="true" />
    <overlay package="com.oem.green.theme" enabled="true" />
</config>"

Для <overlay> требуется атрибут package , указывающий, какой пакет наложения настраивается. Необязательный атрибут enabled определяет, включено ли наложение по умолчанию (по умолчанию — false ). Необязательный атрибут mutable определяет, является ли наложение изменяемым и может ли его включенное состояние изменяться программно во время выполнения (по умолчанию — true ). Наложения, не перечисленные в файле конфигурации, являются изменяемыми и по умолчанию отключены.

Приоритет наложения

Когда несколько наложений переопределяют одни и те же ресурсы, важен порядок наложений. Наложение имеет более высокий приоритет, чем наложения с конфигурациями, предшествующими его собственной конфигурации. Порядок приоритета наложений в разных разделах (от наименьшего к наибольшему приоритету) следующий.

  • system
  • vendor
  • oem
  • odm
  • product
  • system_ext

Объединение файлов

Использование тегов <merge> позволяет объединять другие файлы конфигурации в указанной позиции с файлом конфигурации. Атрибут path тега представляет собой путь к файлу для слияния относительно каталога, содержащего файлы конфигурации наложения.

Использование атрибутов манифеста (статические RRO)

В Android 10 или более ранней версии неизменность и приоритет наложения настраиваются с помощью следующих атрибутов манифеста.

  • android:isStatic . Когда значение этого логического атрибута установлено в true , наложение включено по умолчанию и является неизменяемым, что предотвращает отключение наложения.

  • android:priority . Значение этого числового атрибута (который влияет только на статические наложения) настраивает приоритет наложения, когда несколько статических наложений нацелены на одно и то же значение ресурса. Большее число указывает на более высокий приоритет.

В следующем коде показан пример AndroidManifest.xml .

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.overlay">
    <application android:hasCode="false" />
    <overlay android:targetPackage="com.example.target"
                   android:isStatic="true"
                   android:priority="5"/>
</manifest>

Изменения в Android 11

В Android 11 или более поздней версии, если файл конфигурации находится в partition/overlay/config/config.xml , оверлеи настраиваются с использованием этого файла, а android:isStatic и android:priority не влияют на оверлеи, расположенные в разделе. Определение файла конфигурации оверлея в любом разделе обеспечивает приоритет раздела оверлея.

Кроме того, в Android 11 и более поздних версиях удалена возможность использования статических наложений, чтобы влиять на значения ресурсов, считанные во время установки пакета. Для общего случая использования статических наложений для изменения значения логических значений, которые настраивают включенное состояние компонента, используйте тег SystemConfig <component-override> (новый в Android 11).

Отладка оверлеев

Чтобы вручную включить, отключить и создать дамп наложений, используйте следующую команду оболочки менеджера наложений.

adb shell cmd overlay

OverlayManagerService использует idmap2 для сопоставления идентификаторов ресурсов в целевом пакете с идентификаторами ресурсов в пакете наложения. Сгенерированные сопоставления идентификаторов хранятся в /data/resource-cache/ . Если ваш оверлей работает неправильно, найдите соответствующий файл idmap для вашего оверлея в /data/resource-cache/ , а затем выполните следующую команду.

adb shell idmap2 dump --idmap-path [file]

Эта команда печатает сопоставление ресурсов, как показано ниже.

[target res id] - > [overlay res id] [resource name]
0x01040151 -> 0x01050001 string/config_dozeComponent
0x01040152 -> 0x01050002 string/config_dozeDoubleTapSensorType
0x01040153 -> 0x01050003 string/config_dozeLongPressSensorType