런타임 리소스 오버레이(RRO)

Android 10 이상에서는 Android Asset Packaging Tool 2(AAPT2)를 사용하여 런타임 시 오버레이하기 위해 APK에서 런타임 리소스 오버레이(RRO)를 허용할 리소스를 정의할 수 있습니다.

RRO 구현

Android 10에는 일반 이름으로 함께 오버레이해야 하는 리소스를 수집하는 새로운 <overlayable> XML 태그가 도입되었습니다. 아래 예에서 string/foointeger/bar는 기기 모양의 테마를 설정하는 데 사용하는 리소스입니다. 이러한 리소스를 오버레이하려면 오버레이 가능한 리소스의 컬렉션을 이름별로 명확하게 타겟팅해야 합니다.

APK는 여러 <overlayable> 태그를 정의할 수 있지만 각 태그에는 패키지에 속한 고유 이름이 있어야 합니다. 예를 들면 다음과 같습니다.

  • 두 개의 다른 패키지에서 모두 <overlayable name="foo">를 정의해도 됩니다.

  • 단일 APK에 두 개의 <overlayable name="foo"> 블록이 있으면 안 됩니다.

res/values/overlayable.xml 파일 예:

<!-- 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>

AndroidManifest.xml 파일의 오버레이 예:

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
       package="com.my.theme.overlay">

       <!-- This overlay will override the ThemeResources resources -->
       <overlay android:targetPackage="android" android:targetName="ThemeResources">
</manifest>

애플리케이션에서 <overlayable> 태그를 정의하면 정의한 애플리케이션을 타겟팅하는 오버레이는 다음을 충족해야 합니다.

  • 'targetName'을 지정해야 합니다.

  • 태그 내에 나열된 리소스만 오버레이할 수 있습니다.

  • <overlayable> 이름을 하나만 타겟팅할 수 있습니다.

오버레이 가능한 리소스를 노출하지만 특정 <overlayable> 태그를 타겟팅하는 데 android:targetName을 사용하지 않는 패키지를 타겟팅하는 오버레이는 사용 설정할 수 없습니다.

정책 제한

<policy> 태그는 오버레이 가능한 리소스에 제한을 적용합니다. type 속성은 포함된 리소스를 재정의하기 위해 오버레이에서 충족해야 하는 정책을 지정합니다. 지원되는 유형은 다음과 같습니다.

  • public - 모든 오버레이가 리소스를 재정의할 수 있습니다.

  • system - 시스템 파티션의 모든 오버레이가 리소스를 재정의할 수 있습니다.

  • vendor - 공급업체 파티션의 모든 오버레이가 리소스를 재정의할 수 있습니다.

  • product - 제품 파티션의 모든 오버레이가 리소스를 재정의할 수 있습니다.

  • signature - 타겟 APK와 동일하게 서명된 모든 오버레이가 리소스를 재정의할 수 있습니다.

res/values/overlayable.xml 파일의 <policy> 태그 예:

<overlayable name=”ThemeResources”>
   <policy type=“product” >
       <item type=”string” name=”foo” />
   </policy>
   <policy type=“system|signature”  >
       <item type=”string” name=”bar” />
       <item type=”string” name=”baz” />
   </policy>
</overlayable>

여러 정책을 지정하려면 수직 막대(|)를 구분 문자로 사용하세요. 여러 정책을 지정하면 오버레이는 <policy> 태그 내에 나열된 리소스를 재정의하기 위해 하나의 정책만 충족하면 됩니다.

RRO 관리

  • 오버레이를 사용 설정, 중지 또는 재정렬하려면 CHANGE_OVERLAY_PACKAGES 권한이 있어야 합니다.

  • 현재 사용자가 아닌 사용자에게 오버레이를 사용 설정하려면 INTERACT_ACROSS_USERS 권한이 있어야 합니다.

규정 준수

프레임워크의 오버레이 가능한 리소스 선언은 주어진 SDK 수준의 모든 Android 기기에서 일관되어야 합니다. 오버레이 가능한 프레임워크 리소스의 정의에 영향을 미치는 모든 추가, 삭제, 정책 변경 및 기타 변경사항은 AOSP에 병합되어 모든 Android 기기에서 일관성을 유지해야 합니다. CTS 테스트는 Android 프레임워크의 오버레이 가능한 API가 AOSP에 정의된 오버레이 가능한 정의에서 벗어나지 않도록 합니다.

RRO 설치

Android 9 이하에서 PackageManagerService로 오버레이를 설치하려면 오버레이가 사전 설치되거나 플랫폼 서명으로 서명되어야 합니다. Android 9 기기의 Android 9 오버레이는 기기를 Android 10으로 업그레이드한 후에도 정상적으로 계속 작동합니다.

Android 10 이상에서 플랫폼 서명으로 서명되지 않은 오버레이는 데이터 파티션에 설치할 수 있습니다. 오버레이 가능한 리소스를 선언하지 않은 패키지를 타겟팅하는 오버레이는 사전 설치되거나 타겟 패키지와 동일하게 서명되어야 사용 설정할 수 있습니다.

디버깅

다음을 사용하여 오버레이와 타겟 애플리케이션을 기기에 설치합니다.

adb install

오버레이를 수동으로 사용 설정, 중지, 덤프하려면 다음과 같은 오버레이 관리자 셸 명령어를 사용합니다.

adb shell cmd overlay

OverlayManagerServiceidmap 2를 사용하여 타겟 패키지의 리소스 ID를 오버레이 패키지의 리소스 ID에 매핑합니다. 생성된 ID 매핑은 /data/resource-cache/에 저장됩니다.

오버레이가 올바르게 작동하지 않으면 /data/resource-cache/에서 오버레이에 상응하는 idmap 파일을 찾아서 다음과 같이 실행합니다.

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
...

오버레이 가능한 XML 스키마

<?xml version="1.0" encoding="UTF-8"?>

<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
 elementFormDefault="qualified">

 <xs:element name="overlayable">
   <xs:complexType>
     <xs:sequence>
       <xs:element maxOccurs="unbounded" ref="policy"/>
     </xs:sequence>
     <xs:attribute name="actor" type="xs:anyURI"/>
     <xs:attribute name="name" use="required" type="xs:NCName"/>
   </xs:complexType>
 </xs:element>

 <xs:element name="policy">
   <xs:complexType>
     <xs:sequence>
       <xs:element maxOccurs="unbounded" ref="item"/>
     </xs:sequence>
     <xs:attribute name="type" use="required"/>
   </xs:complexType>
 </xs:element>

 <xs:element name="item">
   <xs:complexType>
     <xs:attribute name="name" use="required"/>
     <xs:attribute name="type" use="required" type="xs:NCName"/>
   </xs:complexType>
 </xs:element>
</xs:schema>