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

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

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

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

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

נוספה שיטה חדשה של @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.