שמירת חבילות APK במטמון

במסמך הזה מתואר עיצוב של פתרון לשמירת APK במטמון להתקנה מהירה של אפליקציות שנטענו מראש במכשיר שתומך במחיצות A/B.

יצרני ציוד מקורי יכולים למקם טעינות מראש ואפליקציות פופולריות במטמון ה-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/+/android16-release/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/
    
    זוהי מבנה הספריות הסופי במכשירים. יצרני ציוד מקורי יכולים לבחור כל גישה להטמעה, כל עוד מבנה הקובץ הסופי זהה לזה שמתואר למעלה.

גישה 2. תוכן בתמונה של נתוני משתמש שהועלתה למכשיר במפעל

הגישה החלופית הזו מניחה שהתוכן שנטען מראש כבר כלול בספרייה /data/preloads במחיצה /data.

יתרון: פועל מיד – אין צורך לבצע התאמות אישיות במכשיר כדי להעתיק קבצים בהפעלה הראשונה. התוכן כבר נמצא בחלוקה /data.

חיסרון: התוכן שנטען מראש יימחק אחרי איפוס להגדרות המקוריות. יכול להיות שזה מקובל על חלק מהיצרנים, אבל לא תמיד זה מתאים ליצרני ציוד מקורי (OEM) שמבצעים איפוס להגדרות המפעל במכשירים אחרי בדיקות בקרת איכות.

נוספה שיטה חדשה של @SystemApi, ‏ getPreloadsFileCache(), אל android.content.Context. היא מחזירה נתיב מוחלט לספרייה ספציפית לאפליקציה במטמון שנטען מראש.

נוספה שיטה חדשה, IPackageManager.deletePreloadsFileCache, שמאפשרת למחוק את ספריית הטעינות מראש כדי לפנות את כל המקום. אפשר להפעיל את ה-method רק באפליקציות עם 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. אם צריך, מדמים את ספריית מטמון הקבצים ואת התוכן שלה (נדרשות גם הרשאות root):
    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.