APK 캐싱

이 문서에서는 A/B 파티션을 지원하는 기기에 미리 로드된 앱을 빠르게 설치할 수 있도록 APK 캐싱 솔루션 설계를 설명합니다.

OEM은 사용자 대상 데이터 공간에 영향을 미치지 않고 새로운 A/B 파티션 기기의 거의 비어 있는 B 파티션에 저장된 APK 캐시에 미리 로드한 항목 및 인기 있는 앱을 배치할 수 있습니다. APK 캐시를 기기에서 사용할 수 있게 되면 Google Play에서 APK 파일을 다운로드하지 않고도 새로운 기기나 초기화된 기기를 거의 즉시 사용할 수 있습니다.

사용 사례

  • 빠른 설치를 위해 미리 로드된 앱을 B 파티션에 저장
  • 신속한 복원을 위해 인기 있는 앱을 B 파티션에 저장

기본 요건

이 기능을 사용하려면 기기에 다음이 필요합니다.

  • Android 8.1(O MR1) 버전이 설치됨
  • A/B 파티션이 구현됨

미리 로드된 콘텐츠는 처음 부팅하는 중에만 복사할 수 있습니다. 이는 A/B 시스템 업데이트를 지원하는 기기에서는 B 파티션이 실제로 시스템 이미지 파일을 저장하지 않고 소매 데모 리소스, OAT 파일, APK 캐시 등과 같은 미리 로드된 콘텐츠를 저장하기 때문입니다. 리소스가 /data 파티션에 복사된 후(처음 부팅 시 발생함) B 파티션은 시스템 이미지의 업데이트된 버전을 다운로드하기 위해 OTA(무선) 업데이트에 사용됩니다.

따라서 APK 캐시는 OTA를 통해 업데이트할 수 없고 공장에서만 미리 로드할 수 있습니다. 초기화는 /data 파티션에만 영향을 미칩니다. OTA 이미지가 다운로드될 때까지 시스템 B 파티션에는 미리 로드된 콘텐츠가 남아 있습니다. 초기화가 완료되면 시스템에서 다시 최초 부팅을 진행합니다. 즉, OTA 이미지가 B 파티션에 다운로드되고 기기가 초기화되면 APK 캐싱을 사용할 수 없습니다.

구현

접근 방법 1. system_other 파티션의 콘텐츠

장점: 미리 로드된 콘텐츠는 초기화 후 손실되지 않으며 재부팅 후에 B 파티션에서 복사됩니다.

단점: B 파티션에 공간이 필요합니다. 초기화 후 부팅하면 미리 로드된 콘텐츠를 복사하는 데 추가 시간이 필요합니다.

처음 부팅 중 미리 로드된 항목을 복사하기 위해 시스템은 /system/bin/preloads_copy.sh에 있는 스크립트를 호출합니다. 이 스크립트는 단일 인수(system_b 파티션의 읽기 전용 마운트 지점 경로)로 호출됩니다.

이 기능을 구현하려면 기기별로 변경하세요. 다음은 Marlin의 예입니다.

  1. 복사를 실행하는 스크립트를 device-common.mk 파일(이 경우에는 device/google/marlin/device-common.mk)에 추가합니다. 예를 들어 다음과 같습니다.
    # Script that copies preloads directory from system_other to data partition
    PRODUCT_COPY_FILES += \
        device/google/marlin/preloads_copy.sh:system/bin/preloads_copy.sh
    
    샘플 스크립트 소스는 device/google/marlin/preloads_copy.sh에서 찾을 수 있습니다.
  2. 필요한 /data/preloads 디렉터리 및 하위 디렉터리를 만들도록 init.common.rc 파일을 수정합니다.
    mkdir /data/preloads 0775 system system
    mkdir /data/preloads/media 0775 system system
    mkdir /data/preloads/demo 0775 system system
    
    예제 init 파일 소스는 device/google/marlin/init.common.rc에서 찾을 수 있습니다.
  3. preloads_copy.te 파일에서 새 SELinux 도메인을 정의합니다.
    type preloads_copy, domain, coredomain;
    type preloads_copy_exec, exec_type, vendor_file_type, file_type;
    
    init_daemon_domain(preloads_copy)
    
    allow preloads_copy shell_exec:file rx_file_perms;
    allow preloads_copy toolbox_exec:file rx_file_perms;
    allow preloads_copy preloads_data_file:dir create_dir_perms;
    allow preloads_copy preloads_data_file:file create_file_perms;
    allow preloads_copy preloads_media_file:dir create_dir_perms;
    allow preloads_copy preloads_media_file:file create_file_perms;
    
    # Allow to copy from /postinstall
    allow preloads_copy system_file:dir r_dir_perms;
    
    예제 SELinux 도메인 파일은 /device/google/marlin/+/master/sepolicy/preloads_copy.te에서 찾을 수 있습니다.
  4. /sepolicy/file_contexts 파일에 도메인을 등록합니다.
    /system/bin/preloads_copy\.sh     u:object_r:preloads_copy_exec:s0
    
    예제 SELinux 컨텍스트 파일은 device/google/marlin/sepolicy/preloads_copy.te에서 찾을 수 있습니다.
  5. 빌드할 때 미리 로드된 콘텐츠가 있는 디렉터리를 system_other 파티션에 복사해야 합니다.
    # Copy contents of preloads directory to system_other partition
    PRODUCT_COPY_FILES += \
        $(call find-copy-subdir-files,*,vendor/google_devices/marlin/preloads,system_other/preloads)
    
    이는 공급업체 Git 저장소(이 경우에는 vendor/google_devices/marlin/preloads)의 APK 캐시 리소스를 나중에 기기 첫 부팅 시 /data/preloads로 복사할 system_other 파티션의 위치로 복사하도록 허용하는 Makefile 내 변경을 보여주는 예입니다. 이 스크립트는 system_other 이미지를 준비하기 위해 빌드 시 실행됩니다. 미리 로드된 콘텐츠는 vendor/google_devices/marlin/preloads에서 사용할 수 있습니다. OEM은 실제 저장소 이름/경로를 자유롭게 선택할 수 있습니다.
  6. APK 캐시는 /data/preloads/file_cache에 있으며 레이아웃은 다음과 같습니다.
    /data/preloads/file_cache/
        app.package.name.1/
              file1
              fileN
        app.package.name.N/
    
    이는 기기에서 최종 디렉터리 구조입니다. 최종 파일 구조가 위에서 설명한 대로 복제되는 한 OEM은 구현 방법을 자유롭게 선택할 수 있습니다.

접근 방법 2. 출고 시 플래싱된 사용자 데이터 이미지의 콘텐츠

이 대체 접근 방식은 미리 로드된 콘텐츠가 /data 파티션의 /data/preloads 디렉터리에 이미 포함되어 있다고 가정합니다.

장점: 처음 부팅 시 파일을 복사하도록 기기를 맞춤설정하지 않아도 바로 사용할 수 있습니다. 콘텐츠가 이미 /data 파티션에 있습니다.

단점: 미리 로드된 콘텐츠는 초기화 후 손실됩니다. 경우에 따라 이러한 방식이 적용될 수 있지만 품질 관리 검사를 실행한 후 기기를 초기화한 OEM의 경우에는 이 방식이 작동하지 않을 수도 있습니다.

새 @SystemApi 메서드인 getPreloadsFileCache()android.content.Context에 추가되었습니다. 미리 로드된 캐시의 애플리케이션별 디렉터리의 절대 경로를 반환합니다.

preloads 디렉터리를 삭제하여 모든 공간을 회수할 수 있는 IPackageManager.deletePreloadsFileCache라는 새로운 메서드가 추가되었습니다. 이 메서드는 SYSTEM_UID 즉, 시스템 서버 또는 설정이 있는 앱에서만 호출할 수 있습니다.

앱 준비

권한이 있는 애플리케이션만 preloads 캐시 디렉터리에 액세스할 수 있습니다. 이와 같이 액세스하려면 앱을 /system/priv-app 디렉터리에 설치해야 합니다.

유효성 검사

  • 처음 부팅한 후 콘텐츠는 기기의 /data/preloads/file_cache 디렉터리에 있습니다.
  • 기기의 저장용량이 부족하면 file_cache/ 디렉터리의 콘텐츠를 삭제해야 합니다.

APK 캐시 테스트에는 예제 ApkCacheTest 앱을 사용합니다.

  1. 루트 디렉터리에서 다음 명령어를 실행하여 앱을 빌드합니다.
    make ApkCacheTest
    
  2. 앱을 권한이 부여된 애플리케이션으로 설치합니다. 권한이 있는 앱만 APK 캐시에 액세스할 수 있습니다. 이 경우 루팅된 기기가 필요합니다.
    adb root && adb remount
    adb shell mkdir /system/priv-app/ApkCacheTest
    adb push $ANDROID_PRODUCT_OUT/data/app/ApkCacheTest/ApkCacheTest.apk /system/priv-app/ApkCacheTest/
    adb shell stop && adb shell start
    
  3. 필요한 경우 파일 캐시 디렉터리와 그 내용을 시뮬레이션합니다(루트 권한도 필요).
    adb shell mkdir -p /data/preloads/file_cache/com.android.apkcachetest
    adb shell restorecon -r /data/preloads
    adb shell "echo "Test File" > /data/preloads/file_cache/com.android.apkcachetest/test.txt"
    
  4. 앱을 테스트합니다. 앱을 설치하고 테스트 file_cache 디렉터리를 만든 후 ApkCacheTest 앱을 엽니다. test.txt 파일 하나와 그 내용이 표시되어야 합니다. 다음 스크린샷에서 사용자 인터페이스에 결과가 어떻게 나타나는지 확인하세요.

    그림 1. ApkCacheTest 결과