שמירת 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. עד להורדת קובץ האימג' ב-OTA, התוכן שהוטען מראש עדיין נמצא במחיצה B של המערכת. אחרי האיפוס להגדרות המקוריות, המערכת תעבור שוב את ההפעלה הראשונה. כלומר, האחסון במטמון של קובצי 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. בזמן ה-build, צריך להעתיק את הספרייה עם התוכן שהוטען מראש למחיצה 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 כשהמכשיר יופעל בפעם הראשונה. הסקריפט הזה פועל בזמן ה-build כדי להכין את קובץ האימג' system_other. התוכן שנטען מראש אמור להיות זמין בכתובת vendor/google_devices/marlin/preloads. יצרני הציוד המקורי יכולים לבחור את השם או הנתיב של המאגר בפועל.
  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.

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

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

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

הכנת האפליקציה

רק אפליקציות עם הרשאות יכולות לגשת לספריית המטמון של פריטים שהועלו מראש. כדי לקבל גישה כזו, צריך להתקין את האפליקציות בספרייה /system/priv-app.

אימות

  • אחרי ההפעלה הראשונה, צריך להיות תוכן בספרייה /data/preloads/file_cache במכשיר.
  • אם במכשיר נגמר נפח האחסון, צריך למחוק את התוכן בתיקייה file_cache/.

משתמשים באפליקציית הדוגמה ApkCacheTest כדי לבדוק את המטמון של קובצי ה-APK.

  1. מריצים את הפקודה הבאה מהספרייה ברמה הבסיסית (root) כדי ליצור את האפליקציה:
    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.