עדכוני מערכת דינמיים

עדכוני מערכת דינמיים (DSU) מאפשרים ליצור קובץ אימג' של מערכת Android, שמשתמשים יכולים להוריד מהאינטרנט ולנסות בלי סכנה של פגיעה בקובץ האימג' הנוכחי של המערכת. במסמך הזה מוסבר איך לתמוך ב-DSU.

דרישות הליבה

מידע על דרישות הליבה מופיע במאמר הטמעה של מחיצות דינמיות.

בנוסף, DSU מסתמך על תכונת הליבה device-mapper-verity‏ (dm-verity) כדי לאמת את קובץ האימג' של מערכת Android. לכן צריך להפעיל את הגדרות הליבה הבאות:

  • CONFIG_DM_VERITY=y
  • CONFIG_DM_VERITY_FEC=y

דרישות לגבי מחיצות

החל מ-Android 11, כדי להשתמש ב-DSU, צריך להשתמש במערכת הקבצים F2FS או ext4 במחיצה /data. פורמט F2FS מספק ביצועים טובים יותר ומומלץ להשתמש בו, אבל ההבדל אמור להיות זניח.

ריכזנו כאן כמה דוגמאות למשך הזמן של עדכון מערכת דינמי במכשיר Pixel:

  • באמצעות F2FS:
    • 109 שניות, 8GB משתמש, 867MB מערכת, סוג מערכת קבצים: F2FS: encryption=aes-256-xts:aes-256-cts
    • 104s, משתמש 8G, מערכת 867M, סוג מערכת הקבצים: F2FS: encryption=ice
  • שימוש ב-ext4:
    • 135 שניות, 8GB משתמש, 867M מערכת, סוג מערכת קבצים: ext4: encryption=aes-256-xts:aes-256-cts

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

המחיצה metadata (16MB או יותר) נדרשת לאחסון נתונים שקשורים לתמונות המותקנות. צריך לטעון אותו במהלך הטעינה בשלב הראשון.

במחיצה userdata צריך להשתמש במערכת קבצים מסוג F2FS או ext4. כשמשתמשים ב-F2FS, צריך לכלול את כל התיקונים שקשורים ל-F2FS שזמינים בליבת Android המשותפת.

ה-DSU פותח ונבדק באמצעות ליבה/קומון 4.9. מומלץ להשתמש בליבה (kernel) 4.9 ואילך בשביל התכונה הזו.

התנהגות HAL של הספק

Weaver HAL

HAL של האריגה מספק מספר קבוע של משבצות לאחסון מפתחות משתמש. ה-DSU תופס שני חריצי מפתחות נוספים. אם ל-OEM יש HAL של weaver, צריכים להיות לו מספיק משבצות לתמונת מערכת גנרית (GSI) ולתמונת מארח.

שומר שער HAL

ה-Gatekeeper HAL צריך לתמוך בערכי USER_ID גדולים, כי ה-GSI מיסט UID ל-HAL ב-1000000+.

אימות האתחול

אם רוצים לתמוך בהפעלה של תמונות GSI למפתחים במצב נעול בלי להשבית את האתחול המאומת, צריך לכלול מפתחות GSI למפתחים. לשם כך, מוסיפים את השורה הבאה לקובץ device/<device_name>/device.mk:

$(call inherit-product, $(SRC_TARGET_DIR)/product/developer_gsi_keys.mk)

הגנה מפני רולבק

כשמשתמשים ב-DSU, קובץ האימג' של מערכת Android שהורדתם צריך להיות חדש יותר מקובץ האימג' הנוכחי של המערכת במכשיר. לשם כך, משווים בין הרמות של תיקוני האבטחה בהפעלה של Android אומת (AVB) במתאר מאפיין AVB של שתי תמונות המערכת: Prop: com.android.build.system.security_patch -> '2019-04-05'.

במכשירים שלא משתמשים ב-AVB, צריך להוסיף את רמת תיקון האבטחה של קובץ האימג' הנוכחי של המערכת ל-cmdline או ל-bootconfig של הליבה באמצעות תוכנת האתחול: androidboot.system.security_patch=2019-04-05.

דרישות חומרה

כשמפעילים מכונה של DSU, מוקצים שני קבצים זמניים:

  • מחיצה לוגית לאחסון GSI.img (1~1.5 G)
  • מחיצת /data ריקה בנפח 8GB היא ארגז החול להפעלת GSI

מומלץ להקצות לפחות 10GB של מקום פנוי לפני שמפעילים מכונה של DSU. DSU תומך גם בהקצאה מכרטיס SD. כאשר מוכנס כרטיס SD, הוא מקבל את העדיפות הגבוהה ביותר להקצאה. תמיכה בכרטיס SD חיונית למכשירים עם הספק נמוך יותר, שאולי אין להם מספיק אחסון פנימי. אם יש כרטיס SD, חשוב לוודא שהוא לא אומץ. DSU לא תומך בכרטיסי SD שאימצו.

ממשקי קצה זמינים

אפשר להפעיל את DSU באמצעות adb, אפליקציית OEM או מנטען DSU בקליק אחד (ב-Android מגרסה 11 ואילך).

הפעלת DSU באמצעות adb

כדי להפעיל DSU באמצעות adb, מזינים את הפקודות הבאות:

$ simg2img out/target/product/.../system.img system.raw
$ gzip -c system.raw > system.raw.gz
$ adb push system.raw.gz /storage/emulated/0/Download
$ adb shell am start-activity \
-n com.android.dynsystem/com.android.dynsystem.VerificationActivity  \
-a android.os.image.action.START_INSTALL    \
-d file:///storage/emulated/0/Download/system.raw.gz  \
--el KEY_SYSTEM_SIZE $(du -b system.raw|cut -f1)  \
--el KEY_USERDATA_SIZE 8589934592

הפעלת DSU באמצעות אפליקציה

נקודת הכניסה הראשית ל-DSU היא ה-API של android.os.image.DynamicSystemClient.java:

public class DynamicSystemClient {


...
...

     /**
     * Start installing DynamicSystem from URL with default userdata size.
     *
     * @param systemUrl A network URL or a file URL to system image.
     * @param systemSize size of system image.
     */
    public void start(String systemUrl, long systemSize) {
        start(systemUrl, systemSize, DEFAULT_USERDATA_SIZE);
    }

צריך לצרף את האפליקציה הזו לחבילה או להתקין אותה מראש במכשיר. מכיוון ש-DynamicSystemClient הוא ממשק API של מערכת, אי אפשר ליצור את האפליקציה באמצעות ממשק ה-API הרגיל של ה-SDK, ואי אפשר לפרסם אותה ב-Google Play. מטרת האפליקציה הזו היא:

  1. אחזור רשימת תמונות וכתובת ה-URL התואמת באמצעות סכמה שהוגדרה על ידי הספק.
  2. להתאים את התמונות ברשימה למכשיר ולהציג תמונות תואמות למשתמש לבחירה.
  3. הפעלת DynamicSystemClient.start באופן הבא:

    DynamicSystemClient aot = new DynamicSystemClient(...)
       aot.start(
            ...URL of the selected image...,
            ...uncompressed size of the selected image...);
    
    

כתובת ה-URL מפנה לקובץ תמונת מערכת עם gzip ולא חלש, שאפשר ליצור באמצעות הפקודות הבאות:

$ simg2img ${OUT}/system.img ${OUT}/system.raw
$ gzip ${OUT}/system.raw
$ ls ${OUT}/system.raw.gz

שם הקובץ צריך להיות בפורמט הבא:

<android version>.<lunch name>.<user defined title>.raw.gz

לדוגמה:

  • o.aosp_taimen-userdebug.2018dev.raw.gz
  • p.aosp_taimen-userdebug.2018dev.raw.gz

מעבד DSU בלחיצה אחת

ב-Android 11 אנחנו משיקים את טוען ה-DSU בלחיצה אחת, שהוא ממשק קצה בהגדרות למפתחים.

הפעלת טוען ה-DSU

איור 1. הפעלת מערך האתחול של DSU

כשהמפתח לוחץ על הלחצן DSU Loader, מתבצע אחזור של מתאר DSU JSON שהוגדר מראש מהאינטרנט, וכל התמונות הרלוונטיות מוצגות בתפריט הצף. בוחרים קובץ אימג' כדי להתחיל את התקנת DSU, וההתקדמות תוצג בסרגל ההתראות.

התקדמות ההתקנה של תמונת ה-DSU

איור 2. התקדמות ההתקנה של תמונת ה-DSU

כברירת מחדל, מערך הטעינה של DSU טוען מתאר JSON שמכיל את קובצי האימג' של GSI. בקטעים הבאים מוסבר איך ליצור חבילות DSU חתומות על ידי ה-OEM ולטעון אותן מטעינת ה-DSU.

סימון תכונה

התכונה DSU נמצאת ב-feature flag‏ settings_dynamic_android. לפני שמשתמשים ב-DSU, צריך לוודא שסימן ההפעלה של התכונה הרלוונטית מופעל.

הפעלת התכונה הניסיונית.

איור 3. הפעלת הסימון של התכונה

יכול להיות שממשק המשתמש של ה-feature flag לא יהיה זמין במכשיר שבו פועל build של משתמש. במקרה כזה, משתמשים במקום זאת בפקודה adb:

$ adb shell setprop persist.sys.fflag.override.settings_dynamic_system 1

תמונות של המערכת המארחת של הספק ב-GCE (אופציונלי)

אחד ממיקומי האחסון האפשריים של קובצי האימג' של המערכת הוא הקטגוריה של Google Compute Engine‏ (GCE). האדמין של הגרסה משתמש במסוף האחסון של GCP כדי להוסיף, למחוק או לשנות את תמונת המערכת שפורסמה.

התמונות צריכות להיות גלויות לכולם, כפי שמוצג כאן:

גישה ציבורית ב-GCE

איור 4. גישה ציבורית ב-GCE

במסמכי העזרה של Google Cloud מוסבר איך להפוך פריט לציבורי.

DSU עם כמה מחיצות בקובץ ZIP

החל מגרסה 11 של Android, ל-DSU יכול להיות יותר ממחיצה אחת. לדוגמה, היא יכולה להכיל product.img בנוסף ל-system.img. כשהמכשיר יופעל, השלב הראשון init מזהה את מחיצות ה-DSU שהותקנו ומחליף את המחיצה במכשיר באופן זמני, כשה-DSU המותקן מופעל. חבילת ה-DSU עשויה להכיל מחיצה שאין לה מחיצה תואמת במכשיר.

תהליך DSU עם מחיצות מרובות

איור 5. תהליך DSU עם מחיצות מרובות

DSU בחתימה של יצרן הציוד המקורי

כדי לוודא שכל התמונות שפועלות במכשיר אושרו על ידי יצרן המכשיר, כל התמונות בחבילת ה-DSU חייבות להיות חתומות. לדוגמה, נניח שיש חבילת DSU שמכילה שתי תמונות של מחיצות, כמו בדוגמה הבאה:

dsu.zip {
    - system.img
    - product.img
}

צריך לחתום על system.img וגם על product.img באמצעות מפתח ה-OEM לפני שמוסיפים אותם לקובץ ה-ZIP. השיטה הנפוצה היא להשתמש באלגוריתם אסימטרי, כמו RSA, שבו המפתח הסודי משמש לחתימה על החבילה והמפתח הציבורי משמש לאימות. דיסק ה-RAM של השלב הראשון חייב לכלול את המפתח הציבורי לצורך התאמה, לדוגמה, /avb/*.avbpubkey. אם במכשיר כבר נעשה שימוש ב-AVB, הליך החתימה הקיים יספיק. בקטעים הבאים ממחישים את תהליך החתימה ומדגישים את המיקום של ה-pubkey שמוגדר ב-AVB שמשמש לאימות התמונות שבחבילת ה-DSU.

מתאר JSON של DSU

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

{
    "include": ["https://.../gsi-release/gsi-src.json"]
}

שנית, הפרמיטיבי image משמש לתיאור חבילות DSU שפורסמו. רכיב התמונה כולל מספר מאפיינים:

  • המאפיינים name ו-details הם מחרוזות שמוצגות בתיבת הדו-שיח שהמשתמשים יכולים לבחור.

  • המאפיינים cpu_api, vndk ו-os_version משמשים לבדיקות תאימות, כפי שמתואר בקטע הבא.

  • המאפיין האופציונלי pubkey מתאר את המפתח הציבורי שמתאימים למפתח הסודי שמשמש לחתימה על חבילת ה-DSU. כשהיא מצוינה, שירות ה-DSU יכול לבדוק אם במכשיר יש את המפתח שמשמש לאימות חבילת ה-DSU. כך אפשר למנוע התקנה של חבילת DSU לא מזוהה, למשל התקנה של DSU שנחתם על ידי OEM-A במכשיר שנוצר על ידי OEM-B.

  • המאפיין האופציונלי tos מפנה לקובץ טקסט שמתאר את התנאים וההגבלות של חבילת ה-DSU המתאימה. כשהמפתח בוחר חבילת DSU שבה צוין מאפיין התנאים וההגבלות, נפתחת תיבת הדו-שיח שמופיעה באיור 6 ובה בקשה מהמפתח לאשר את התנאים וההגבלות לפני התקנת חבילת ה-DSU.

    תיבת הדו-שיח &#39;תנאים והגבלות&#39;

    איור 6. תיבת הדו-שיח 'התנאים וההגבלות'

הנה מתאר DSU JSON של GSI:

{
   "images":[
      {
         "name":"GSI+GMS x86",
         "os_version":"10",
         "cpu_abi": "x86",
         "details":"exp-QP1A.190711.020.C4-5928301",
         "vndk":[
            27,
            28,
            29
         ],
         "pubkey":"",
         "tos": "https://dl.google.com/developers/android/gsi/gsi-tos.txt",
         "uri":"https://.../gsi/gsi_gms_x86-exp-QP1A.190711.020.C4-5928301.zip"
      },
      {
         "name":"GSI+GMS ARM64",
         "os_version":"10",
         "cpu_abi": "arm64-v8a",
         "details":"exp-QP1A.190711.020.C4-5928301",
         "vndk":[
            27,
            28,
            29
         ],
         "pubkey":"",
         "tos": "https://dl.google.com/developers/android/gsi/gsi-tos.txt",
         "uri":"https://.../gsi/gsi_gms_arm64-exp-QP1A.190711.020.C4-5928301.zip"
      },
      {
         "name":"GSI ARM64",
         "os_version":"10",
         "cpu_abi": "arm64-v8a",
         "details":"exp-QP1A.190711.020.C4-5928301",
         "vndk":[
            27,
            28,
            29
         ],
         "pubkey":"",
         "uri":"https://.../gsi/aosp_arm64-exp-QP1A.190711.020.C4-5928301.zip"
      },
      {
         "name":"GSI x86_64",
         "os_version":"10",
         "cpu_abi": "x86_64",
         "details":"exp-QP1A.190711.020.C4-5928301",
         "vndk":[
            27,
            28,
            29
         ],
         "pubkey":"",
         "uri":"https://.../gsi/aosp_x86_64-exp-QP1A.190711.020.C4-5928301.zip"
      }
   ]
}

ניהול תאימות

יש כמה מאפיינים שמשמשים לציון התאימות בין חבילת DSU למכשיר המקומי:

  • cpu_api היא מחרוזת שמתארת את ארכיטקטורת המכשיר. המאפיין הזה הוא חובה והוא מושווה למאפיין המערכת ro.product.cpu.abi. הערכים שלהם חייבים להיות זהים.

  • os_version הוא מספר שלם אופציונלי שמציין גרסה של Android. לדוגמה, ב-Android 10, הערך של os_version הוא 10 וב-Android 11, הערך של os_version הוא 11. כשמציינים את המאפיין הזה, הוא צריך להיות שווה למאפיין המערכת ro.system.build.version.release או גדול ממנו. הבדיקה הזו משמשת למניעת אתחול של קובץ אימג' של GSI מגרסה 10 של Android במכשיר של ספק עם Android מגרסה 11, כי אין כרגע תמיכה בכך. מותר להפעיל תמונת Android 11 GSI במכשיר Android 10.

  • vndk הוא מערך אופציונלי שמציין את כל מפתחות ה-VNDK שכלולים בחבילת ה-DSU. כאשר מציינים זאת, טוען ה-DSU בודק אם המספר שחולץ ממאפיין המערכת ro.vndk.version כלול.

ביטול מפתחות DSU למטרות אבטחה

במקרה הנדיר ביותר שבו זוהתה פריצה לזוג המפתחות מסוג RSA שמשמש לחתימה על קובצי האימג' של DSU, צריך לעדכן את ה-ramdisk בהקדם האפשרי כדי להסיר את המפתח שנפרץ. בנוסף לעדכון של מחיצת האתחול, אפשר לחסום מפתחות שנחשפו באמצעות רשימת ביטול של מפתחות DSU (רשימת מפתחות שחורה) מכתובת URL מסוג HTTPS.

רשימת ביטול המפתחות של DSU מכילה רשימה של מפתחות ציבוריים של AVB שבוטלו. במהלך התקנת ה-DSU, המפתחות הציבוריים בתמונות ה-DSU מאומתים באמצעות רשימת הביטולים. אם התמונות מכילות מפתח ציבורי שבוטלו, תהליך ההתקנה של ה-DSU מופסק.

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

frameworks/base/packages/DynamicSystemInstallationService/res/values/strings.xml@key_revocation_list_url

הערך של המחרוזת הוא https://dl.google.com/developers/android/gsi/gsi-keyblacklist.json, שהיא רשימת ביטולים של מפתחות GSI ש-Google פרסמה. אפשר להוסיף שכבה על מחרוזת המשאב הזו ולהתאים אותה אישית, כדי ש-OEMs שמאמצים את תכונת DSU יוכלו לספק ולתחזק רשימת שחור משלהם של מפתחות. כך יצרן הציוד המקורי יכול לחסום מפתחות ציבוריים מסוימים בלי לעדכן את קובץ האימג' של ה-ramdisk במכשיר.

הפורמט של רשימת הביטולים הוא:

{
   "entries":[
      {
         "public_key":"bf14e439d1acf231095c4109f94f00fc473148e6",
         "status":"REVOKED",
         "reason":"Key revocation test key"
      },
      {
         "public_key":"d199b2f29f3dc224cca778a7544ea89470cbef46",
         "status":"REVOKED",
         "reason":"Key revocation test key"
      }
   ]
}
  • public_key הוא תקציר SHA-1 של המפתח שבוטל, בפורמט שמתואר בקטע יצירת AVB pubkey.
  • status מציין את סטטוס הביטול של המפתח. בשלב זה, הערך הנתמך היחיד הוא REVOKED.
  • reason היא מחרוזת אופציונלית שמתארת את הסיבה לביטול.

נוהלי DSU

בקטע הזה מוסבר איך לבצע כמה תהליכי הגדרה של DSU.

יצירת זוג מפתחות חדש

משתמשים בפקודה openssl כדי ליצור זוג מפתחות RSA פרטי/ציבורי בפורמט .pem (לדוגמה, בגודל 2048 ביט):

$ openssl genrsa -out oem_cert_pri.pem 2048
$ openssl rsa -in oem_cert_pri.pem -pubout -out oem_cert_pub.pem

יכול להיות שלא תהיה גישה למפתח הפרטי, והוא נשמר רק במודול אבטחה לחומרה (HSM). במקרה כזה, יכול להיות שיהיה אישור של מפתח ציבורי מסוג x509 אחרי יצירת המפתח. בקטע הוספת מפתח ה-pubkey להתאמה ל-ramdisk מוסבר איך ליצור מפתח ציבורי מסוג AVB מאישור x509.

כדי להמיר אישור x509 לפורמט PEM:

$ openssl x509 -pubkey -noout -in oem_cert_pub.x509.pem > oem_cert_pub.pem

אם האישור הוא כבר קובץ PEM, צריך לדלג על השלב הזה.

הוספת מפתח ה-pubkey של ההתאמה ל-ramdisk

צריך להזין את השדה oem_cert.avbpubkey במסגרת /avb/*.avbpubkey כדי לאמת את חבילת ה-DSU החתומה. קודם צריך להמיר את המפתח הציבורי בפורמט PEM לפורמט של מפתח ציבורי AVB:

$ avbtool extract_public_key --key oem_cert_pub.pem --output oem_cert.avbpubkey

אחר כך כוללים את המפתח הציבורי ב-ramdisk בשלב הראשון בעזרת השלבים הבאים.

  1. צריך להוסיף מודול מוגדר מראש כדי להעתיק את avbpubkey. לדוגמה, מוסיפים את device/<company>/<board>/oem_cert.avbpubkey ואת device/<company>/<board>/avb/Android.mk עם תוכן כזה:

    include $(CLEAR_VARS)
    
    LOCAL_MODULE := oem_cert.avbpubkey
    LOCAL_MODULE_CLASS := ETC
    LOCAL_SRC_FILES := $(LOCAL_MODULE)
    ifeq ($(BOARD_USES_RECOVERY_AS_BOOT),true)
    LOCAL_MODULE_PATH := $(TARGET_RECOVERY_ROOT_OUT)/first_stage_ramdisk/avb
    else
    LOCAL_MODULE_PATH := $(TARGET_RAMDISK_OUT)/avb
    endif
    
    include $(BUILD_PREBUILT)
    
  2. מגדירים את היעד droidcore כך שיהיה תלוי ב-oem_cert.avbpubkey שנוסף:

    droidcore: oem_cert.avbpubkey
    

יצירת מאפיין pubkey של AVB במתאר JSON

השדה oem_cert.avbpubkey נמצא בפורמט הבינארי של מפתח ציבורי AVB. משתמשים ב-SHA-1 כדי להפוך אותו לקריא לפני שמכניסים אותו למתאר ה-JSON:

$ sha1sum oem_cert.avbpubkey | cut -f1 -d ' '
3e62f2be9d9d813ef5........866ac72a51fd20

זה יהיה התוכן של המאפיין pubkey במתאר ה-JSON.

   "images":[
      {
         ...
         "pubkey":"3e62f2be9d9d813ef5........866ac72a51fd20",
         ...
      },

חתימה על חבילת DSU

אפשר להשתמש באחת מהשיטות הבאות כדי לחתום על חבילת DSU:

  • שיטה 1: שימוש חוזר באובייקט שנוצר בתהליך החתימה המקורי של AVB כדי ליצור חבילת DSU. גישה חלופית היא לחלץ את התמונות שכבר חתומות מחבילת הגרסה, ולהשתמש בתמונות החתוכות כדי ליצור את קובץ ה-ZIP ישירות.

  • שיטה 2: משתמשים בפקודות הבאות כדי לחתום על מחיצות DSU אם המפתח הפרטי זמין. כל img בתוך חבילת DSU (קובץ ה-ZIP) חתום בנפרד:

    $ key_len=$(openssl rsa -in oem_cert_pri.pem -text | grep Private-Key | sed -e 's/.*(\(.*\) bit.*/\1/')
    $ for partition in system product; do
        avbtool add_hashtree_footer \
            --image ${OUT}/${partition}.img \
            --partition_name ${partition} \
            --algorithm SHA256_RSA${key_len} \
            --key oem_cert_pri.pem
    done
    

למידע נוסף על הוספת add_hashtree_footer באמצעות avbtool, ראו שימוש ב-avbtool.

אימות מקומי של חבילת ה-DSU

מומלץ לאמת את כל התמונות המקומיות באמצעות המפתח הציבורי של ההתאמה באמצעות הפקודות הבאות:


for partition in system product; do
    avbtool verify_image --image ${OUT}/${partition}.img  --key oem_cert_pub.pem
done

הפלט הצפוי נראה כך:

Verifying image dsu/system.img using key at oem_cert_pub.pem
vbmeta: Successfully verified footer and SHA256_RSA2048 vbmeta struct in dsu/system.img
: Successfully verified sha1 hashtree of dsu/system.img for image of 898494464 bytes

Verifying image dsu/product.img using key at oem_cert_pub.pem
vbmeta: Successfully verified footer and SHA256_RSA2048 vbmeta struct in dsu/product.img
: Successfully verified sha1 hashtree of dsu/product.img for image of 905830400 bytes

יצירה של חבילת DSU

בדוגמה הבאה יוצרים חבילת DSU שמכילה system.img ו-product.img:

dsu.zip {
    - system.img
    - product.img
}

אחרי שחתמתם על שתי התמונות, משתמשים בפקודה הבאה כדי ליצור את קובץ ה-ZIP:

$ mkdir -p dsu
$ cp ${OUT}/system.img dsu
$ cp ${OUT}/product.img dsu
$ cd dsu && zip ../dsu.zip *.img && cd -

התאמה אישית של DSU בקליק אחד

כברירת מחדל, מערך הטעינה של DSU מפנה למטא-נתונים של קובצי אימג' של GSI, שהם https://...google.com/.../gsi-src.json.

יצרני ציוד מקורי יכולים לשנות את הרשימה על ידי הגדרת המאפיין persist.sys.fflag.override.settings_dynamic_system.list שמצביע על מתאר JSON משלהם. לדוגמה, יצרן ציוד מקורי (OEM) יכול לספק מטא-נתונים של JSON שכוללים GSI וגם תמונות קנייניות של OEM, כך:

{
    "include": ["https://dl.google.com/.../gsi-src.JSON"]
    "images":[
      {
         "name":"OEM image",
         "os_version":"10",
         "cpu_abi": "arm64-v8a",
         "details":"...",
         "vndk":[
            27,
            28,
            29
         ],
         "spl":"...",
         "pubkey":"",
         "uri":"https://.../....zip"
      },

}

יכול להיות שיצרן ציוד מקורי יוכל לפרסם בשרשרת מטא-נתונים של ה-DSU שפורסמו, כפי שמוצג באיור 7.

קישור של מטא-נתונים של DSU שפורסמו

איור 7. קישור של מטא-נתונים של DSU שפורסמו