런타임 리소스 오버레이 문제 해결

이 콘텐츠를 사용하여 런타임 리소스 오버레이(RRO)가 Android Automotive 구현에서 의도한 대로 작동하지 않는 문제를 해결하세요.

Android의 RRO에 관한 자세한 내용은 런타임 리소스 오버레이(RRO)를 참고하세요. logcat 출력을 지속적으로 확인하세요. 이 출력은 프로세스 전체에서 발생하는 일에 관한 유용한 정보를 제공할 수 있습니다.

1단계: RRO 나열하기

RRO를 나열하는 방법은 다음과 같습니다.

  1. 다음 명령어를 실행합니다.

    adb shell cmd overlay list --user current

    다음과 같은 출력이 표시됩니다.

    com.android.systemui
    [ ] com.android.theme.icon_pack.rounded.systemui
    [ ] com.android.theme.icon_pack.filled.systemui
    [ ] com.android.theme.icon_pack.circular.systemui
    
    com.android.permissioncontroller
    --- com.android.permissioncontroller.googlecarui.rro
    
  2. RRO가 목록에 표시되는지 확인합니다. 다음 표시기는 RRO 상태를 나타냅니다.

    표시기 RRO 상태
    [ ] 설치되었으며 활성화할 수 있습니다.
    [X] 설치되고 활성화되었습니다.
    --- 설치되었지만 오류가 있습니다.

    오버레이하려는 타겟의 패키지 이름 아래에 RRO가 없다면 RRO가 설치되지 않은 것입니다.

2단계: RRO 사용 및 사용 중지하기

RRO가 설치된 경우 다음 안내를 따르세요.

  1. 다음 명령어를 사용하여 RRO를 사용 또는 사용 중지합니다.

    adb shell cmd overlay [enable/disable] --user current [your RRO package name]

3단계: RRO가 설치되었는지 확인하기

RRO가 기기에 설치되어 있는지 확인하거나 RRO가 사용 설정되지 않은 문제를 해결하는 방법은 다음과 같습니다.

  1. 다음 명령어를 실행합니다.

    adb shell cmd overlay dump [your RRO package name]

    다음과 같은 출력이 표시됩니다.

    com.android.car.rotaryplayground.googlecarui.rro:0 {
      mPackageName...........: com.android.car.rotaryplayground.googlecarui.rro
      mUserId................: 0
      mTargetPackageName.....: com.android.car.rotaryplayground
      mTargetOverlayableName.: car-ui-lib
      mBaseCodePath..........: /product/overlay/googlecarui-com-android-car-rotaryplayground/googlecarui-com-android-car-rotaryplayground.apk
      mState.................: STATE_MISSING_TARGET
      mIsEnabled.............: true
      mIsMutable.............: true
      mPriority..............: 10
      mCategory..............: BypassIdMapV1
    }
    com.android.car.rotaryplayground.googlecarui.rro:10 {
      mPackageName...........: com.android.car.rotaryplayground.googlecarui.rro
      mUserId................: 10
      mTargetPackageName.....: com.android.car.rotaryplayground
      mTargetOverlayableName.: car-ui-lib
      mBaseCodePath..........: /product/overlay/googlecarui-com-android-car-rotaryplayground/googlecarui-com-android-car-rotaryplayground.apk
      mState.................: STATE_MISSING_TARGET
      mIsEnabled.............: true
      mIsMutable.............: true
      mPriority..............: 10
      mCategory..............: BypassIdMapV1
    }
    
  2. RRO를 설치한 사용자를 확인합니다. 위 예에서 RRO는 사용자 0과 사용자 10에서 사용할 수 있습니다(상단 코드 블록의 mUserId 값 참고).

  3. 원하는 사용자에게 RRO를 사용 또는 사용 중지하려면 2단계로 이동합니다.

  4. mState의 값은 다음과 같이 확인할 수 있습니다.

    • STATE_ENABLEDSTATE_ENABLED_IMMUTABLE: RRO가 사용 설정되어 타겟에 적용되었습니다.

    • STATE_MISSING_TARGET: 타겟이 설치되지 않았습니다.

    • STATE_NO_IDMAP: AndroidManifest.xml, overlays.xml 또는 overlayable.xml 파일 설정 방식에 문제가 있습니다. adb logcat을 사용하여 로그를 실행하고 idmap 키워드를 검색하여 오류를 식별합니다. 4단계 및 5단계 확인하기

    • STATE_UNKNOWN: OverlayManagerService에 문제가 있습니다.

4단계: AndroidManifest.xml 확인하기

AndroidManifest.xml 확인 방법은 다음과 같습니다.

  1. targetNametargetPackage를 확인합니다.

    android:targetName은 타겟 애플리케이션에서 정의된 오버레이 가능한 그룹과 동일한 값을 보유해야 합니다. 이는 오버레이를 타겟팅할 때만 필요합니다.

    android:targetPackage는 항상 필요하며 타겟 애플리케이션의 패키지 이름을 포함해야 합니다.

  2. RRO의 정적 여부를 확인합니다. 정적 RRO는 부팅 시 기본적으로 사용 설정됩니다. 동적 RRO는 부팅 시 기본적으로 사용 설정되지 않습니다. 동적 RRO를 사용 설정하는 다른 방법은 런타임 리소스 오버레이(RRO)를 참고하세요.

  3. 정적 RRO의 우선순위를 확인합니다. 동적 RRO 우선순위는 항상 Integer.MAX_VALUE로 설정되며 적용 순서는 사용 설정 시점을 기준으로 합니다.

    여러 RRO가 동일한 타겟에 적용될 수도 있습니다. 우선순위가 높은 RRO가 마지막으로 적용됩니다. 0에서 10 사이를 기준으로, 10이 가장 높고 0이 가장 낮습니다.

5단계: overlays.xml 확인하기

이 단계는 Android 11 이상에만 적용됩니다.

  1. overlays.xml을 확인하여 오버레이하려는 모든 리소스가 이 파일에 정의되어 있는지 검토합니다. 예를 들어, 다음 overlays.xml을 고려해 보세요.

    <overlay>
        <item target="string/app_name" value="@string/overlaid_app_name" />
    </overlay>
    
  2. 다음과 같아야 합니다.

    • 이름이 app_namestring 리소스가 타겟 앱에 있습니다.
    • 이름이 overlaid_app_namestring 리소스가 RRO에 있습니다.
  3. 타겟에 overlayable.xml 파일이 있으면 파일에 app_name이 포함되어 있는지 확인합니다. AndroidManifest.xml 파일에서 올바른 targetName을 사용하는지 확인합니다(4단계).

    예:

    <overlay>
    <item target="layout/car_ui_base_layout_toolbar" value="@layout/car_ui_base_layout_toolbar" />
    <item target="id/car_ui_toolbar_background" value="@id/car_ui_toolbar_background" />
    <item target="attr/layout_constraintTop_toBottomOf" value="@attr/layout_constraintTop_toBottomOf" />
    </overlay>
    

6단계: idmap 덤프하기

이 단계에서는 RRO의 모든 문제가 해결되어야 합니다. 그런 다음 RRO의 idmap을 덤프하여 리소스가 결정되는 방법과 예상과 다른 값으로 결정되는 이유를 알아봅니다.

  1. 기기에서 idmap 경로를 찾는 방법은 다음과 같습니다.

    adb shell
    su
    ls data/resource-cache
  2. 이 파일의 콘텐츠를 덤프하는 방법은 다음과 같습니다.

    idmap2 dump --idmap-path [path to your RRO idmap file]

    출력은 다음과 같이 표시됩니다. 출력에는 오버레이된 리소스 이름과 함께 RRO의 어떤 ID가 타겟의 어떤 ID에 매핑되는지 표시됩니다.

    target apk path : /system/priv-app/CarMediaApp/CarMediaApp.apk
    overlay apk path : /product/overlay/googlecarui-com-android-car-media/googlecarui-com-android-car-media.apk
    0x7f040008 -> 0x7f010000 bool/car_ui_toolbar_logo_fills_nav_icon_space
    0x7f040009 -> 0x7f010001 bool/car_ui_toolbar_nav_icon_reserve_space
    0x7f04000b -> 0x7f010002 bool/car_ui_toolbar_tab_flexible_layout
    0x7f04000c -> 0x7f010003 bool/car_ui_toolbar_tabs_on_second_row
    0x7f09006c -> 0x7f020000 id/car_ui_base_layout_content_container
    0x7f090073 -> 0x7f020001 id/car_ui_recycler_view
    0x7f090074 -> 0x7f020002 id/car_ui_scroll_bar
    0x7f090075 -> 0x7f020003 id/car_ui_scrollbar_page_down
    0x7f090076 -> 0x7f020004 id/car_ui_scrollbar_page_up
    0x7f090077 -> 0x7f020005 id/car_ui_scrollbar_thumb
    0x7f090078 -> 0x7f020006 id/car_ui_scrollbar_track
    0x7f09007a -> 0x7f020007 id/car_ui_toolbar_background
    0x7f09007e -> 0x7f020008 id/car_ui_toolbar_logo
    0x7f090084 -> 0x7f020009 id/car_ui_toolbar_menu_items_container
    0x7f090085 -> 0x7f02000a id/car_ui_toolbar_nav_icon
    0x7f090086 -> 0x7f02000b id/car_ui_toolbar_nav_icon_container
    0x7f090087 -> 0x7f02000c id/car_ui_toolbar_progress_bar
    0x7f090089 -> 0x7f02000d id/car_ui_toolbar_row_separator_guideline
    0x7f09008d -> 0x7f02000e id/car_ui_toolbar_search_view_container
    0x7f09008f -> 0x7f02000f id/car_ui_toolbar_subtitle
    0x7f090092 -> 0x7f020010 id/car_ui_toolbar_tabs
    0x7f090093 -> 0x7f020011 id/car_ui_toolbar_title
    0x7f090094 -> 0x7f020012 id/car_ui_toolbar_title_container
    0x7f090095 -> 0x7f020013 id/car_ui_toolbar_title_logo
    0x7f090096 -> 0x7f020014 id/car_ui_toolbar_title_logo_container
    0x7f0c0024 -> 0x7f030000 layout/car_ui_base_layout_toolbar
    0x7f0c0035 -> 0x7f030001 layout/car_ui_recycler_view
    0x7f0c0038 -> 0x7f030002 layout/car_ui_toolbar
    0x7f0c003f -> 0x7f030003 layout/car_ui_toolbar_two_row
    

다음 명령어를 사용하여 특정 리소스를 검색하여 어떻게 매핑되는지 확인합니다.

adb shell cmd overlay lookup --verbose --user 10 com.android.car.ui.paintbooth com.android.car.ui.paintbooth:color/widget_background

출력은 리소스의 최종 값입니다.

#ff7986cb

APK에서 레이아웃 파일을 덤프하여 위 출력과 일치하도록 결정된 ID를 볼 수도 있습니다.

aapt2 dump xmltree $OUT/system/priv-app/sharedlibraryclient/sharedlibraryclient.apk --file res/layout/activity_main.xml

다음과 같은 출력이 반환됩니다.

N: android=http://schemas.android.com/apk/res/android (line=2)
  N: app=http://schemas.android.com/apk/res-auto (line=2)
    N: lib=http://schemas.android.com/apk/com.android.car.ui.sharedlibrary.test (line=2)
      E: androidx.constraintlayout.widget.ConstraintLayout (line=2)
        A: http://schemas.android.com/apk/res/android:layout_width(0x010100f4)=-1
        A: http://schemas.android.com/apk/res/android:layout_height(0x010100f5)=-1
          E: TextView (line=19)
            A: http://schemas.android.com/apk/res/android:layout_width(0x010100f4)=-2
            A: http://schemas.android.com/apk/res/android:layout_height(0x010100f5)=-2
            A: http://schemas.android.com/apk/res/android:text(0x0101014f)=@0x020f0000
            A: http://schemas.android.com/apk/res-auto:layout_constraintBottom_toBottomOf(0x7f0200fb)=0
            A: http://schemas.android.com/apk/res-auto:layout_constraintLeft_toLeftOf(0x7f02010e)=0
            A: http://schemas.android.com/apk/res-auto:layout_constraintRight_toRightOf(0x7f020112)=0
            A: http://schemas.android.com/apk/res-auto:layout_constraintTop_toTopOf(0x7f020118)=0
          E: com.android.car.ui.sharedlibrary.test.MyRecyclerView (line=28)
            A: http://schemas.android.com/apk/res/android:layout_width(0x010100f4)=-2
            A: http://schemas.android.com/apk/res/android:layout_height(0x010100f5)=-2
            A: http://schemas.android.com/apk/com.android.car.ui.sharedlibrary.test:implClass="HelloWorld!" (Raw: "HelloWorld!")
          E: com.android.car.ui.sharedlibraryclient.CustomView (line=34)
            A: http://schemas.android.com/apk/res/android:layout_width(0x010100f4)=-2
            A: http://schemas.android.com/apk/res/android:layout_height(0x010100f5)=-2
            A: http://schemas.android.com/apk/res-auto:implClass2(0x7f0200e8)="HelloWorld!!" (Raw: "HelloWorld!!")