การแคช APK

เอกสารนี้อธิบายการออกแบบโซลูชันการแคช APK เพื่อการติดตั้งอย่างรวดเร็ว ของแอปที่โหลดไว้ล่วงหน้าในอุปกรณ์ที่รองรับพาร์ติชัน A/B

OEM สามารถใส่การโหลดล่วงหน้าและแอปยอดนิยมไว้ในแคช APK ที่เก็บไว้ใน พาร์ติชัน B ว่างในอุปกรณ์ที่แบ่งพาร์ติชัน A/B ใหม่โดยไม่ส่งผลกระทบต่อ พื้นที่ข้อมูลที่แสดงต่อผู้ใช้ เพิ่มแคช APK ในอุปกรณ์ หรือ อุปกรณ์ที่เพิ่งรีเซ็ตเป็นค่าเริ่มต้นพร้อมใช้งานเกือบจะทันที ต้องการดาวน์โหลดไฟล์ APK จาก Google Play

กรณีการใช้งาน

  • จัดเก็บแอปที่โหลดไว้ล่วงหน้าในพาร์ติชัน B เพื่อการตั้งค่าที่รวดเร็วขึ้น
  • จัดเก็บแอปยอดนิยมในพาร์ติชัน B เพื่อให้กู้คืนได้เร็วขึ้น

สิ่งที่ต้องมีก่อน

อุปกรณ์จะต้องมีสิ่งต่อไปนี้เพื่อใช้ฟีเจอร์นี้

  • ติดตั้ง Android 8.1 (O MR1) แล้ว
  • ใช้พาร์ติชัน A/B แล้ว

สามารถคัดลอกเนื้อหาที่โหลดล่วงหน้าได้เฉพาะระหว่างการเปิดเครื่องครั้งแรกเท่านั้น เนื่องจากบน อุปกรณ์ที่รองรับการอัปเดตระบบ A/B แต่พาร์ติชัน B จะไม่จัดเก็บ ไฟล์ภาพระบบ แต่โหลดเนื้อหา แบบโหลดล่วงหน้า เช่น ทรัพยากรการสาธิตสำหรับร้านค้าปลีก ไฟล์ OAT และแคช APK หลังจากคัดลอกทรัพยากรไปยัง /data แล้ว พาร์ติชัน (ซึ่งเกิดขึ้นเมื่อเปิดเครื่องครั้งแรก) พาร์ติชัน B จะใช้โดยการเชื่อมต่อผ่านอากาศ (OTA) อัปเดตสำหรับการดาวน์โหลดอิมเมจระบบเวอร์ชันที่อัปเดตแล้ว

ดังนั้นแคช APK จึงอัปเดตผ่าน OTA ไม่ได้ จะโหลดล่วงหน้าได้เท่านั้น ที่โรงงานแห่งหนึ่ง การรีเซ็ตเป็นค่าเริ่มต้นจะมีผลกับพาร์ติชัน /data เท่านั้น ระบบ B พาร์ติชันยังมีเนื้อหาที่โหลดไว้ล่วงหน้าจนกว่าจะดาวน์โหลดอิมเมจ OTA หลังจากรีเซ็ตเป็นค่าเริ่มต้น ระบบจะเปิดเครื่องครั้งแรกอีกครั้ง ซึ่งหมายความว่า APK การแคชไม่พร้อมใช้งานหากดาวน์โหลดอิมเมจ OTA ไปยังพาร์ติชัน B และ ระบบจะรีเซ็ตอุปกรณ์เป็นค่าเริ่มต้น

การใช้งาน

วิธีที่ 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. แก้ไขไฟล์ init.common.rc เพื่อสร้าง ไดเรกทอรีและไดเรกทอรีย่อย /data/preloadsที่จำเป็น:
    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. กำหนดโดเมน SELinux ใหม่ในไฟล์ preloads_copy.te: วันที่
    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/+/main/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)
    
    นี่เป็นตัวอย่างของการเปลี่ยนแปลงใน Makefile ที่อนุญาตให้คัดลอกแคช APK จากที่เก็บ Git ของผู้ให้บริการ (ในกรณีนี้คือ Vendor/google_devices/marlin/preloads) ไปยังตำแหน่งบนพาร์ติชัน system_other ซึ่งจะถูกคัดลอกในภายหลังไปยัง /data/preloads เมื่ออุปกรณ์เปิดเครื่องเป็นครั้งแรก สคริปต์นี้ทำงานในเวลาบิลด์เพื่อเตรียมอิมเมจ 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/preloads บนพาร์ติชัน /data

ข้อดี: ใช้งานได้ทันทีโดยไม่ต้องออกแบบอุปกรณ์ การกำหนดค่าเองเพื่อคัดลอกไฟล์เมื่อเปิดเครื่องครั้งแรก เนื้อหาอยู่ใน พาร์ติชัน /data

ข้อเสีย: เนื้อหาที่โหลดล่วงหน้าจะหายไปหลังจากรีเซ็ตเป็นค่าเริ่มต้น ขณะที่ วิธีนี้อาจยอมรับได้สำหรับบางคน แต่อาจใช้ไม่ได้กับ OEM ที่โรงงานผลิตเสมอไป รีเซ็ตอุปกรณ์หลังจากตรวจสอบการควบคุมคุณภาพ

เพิ่มเมธอด @SystemApi ใหม่ getPreloadsFileCache() ไปยัง android.content.Context โดยจะแสดงเส้นทางสัมบูรณ์ไปยัง ไดเรกทอรีที่เจาะจงแอปในแคชที่โหลดไว้ล่วงหน้า

เพิ่มเมธอดใหม่ "IPackageManager.deletePreloadsFileCache" แล้ว ที่อนุญาตให้ลบไดเรกทอรีการโหลดล่วงหน้าเพื่อเรียกคืนพื้นที่ทั้งหมด เมธอดสามารถ ให้เรียกใช้โดยแอปที่มี SYSTEM_UID เช่น เซิร์ฟเวอร์ระบบหรือการตั้งค่าเท่านั้น

การเตรียมแอป

เฉพาะแอปที่ได้รับสิทธิ์เท่านั้นที่จะเข้าถึงไดเรกทอรีแคชการโหลดล่วงหน้าได้ สำหรับกรณีนี้ คุณต้องติดตั้งแอปในไดเรกทอรี /system/priv-app

การตรวจสอบความถูกต้อง

  • หลังจากเปิดเครื่องครั้งแรก อุปกรณ์ควรมีเนื้อหาใน ไดเรกทอรี /data/preloads/file_cache
  • เนื้อหาในไดเรกทอรี file_cache/ ต้องถูกลบหาก อุปกรณ์เหลือพื้นที่เก็บข้อมูลน้อย

ใช้ตัวอย่าง ApkCacheTest แอปสำหรับการทดสอบแคช APK

  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 ไฟล์และเนื้อหาในไฟล์ ดูภาพหน้าจอนี้เพื่อดูว่าผลลัพธ์เหล่านี้จะปรากฏในอินเทอร์เฟซผู้ใช้อย่างไร

    รูปที่ 1 ผลการทดสอบ ApkCacheTest