تنفيذ تحديثات أ/ب

يجب على مصنعي المعدات الأصلية وبائعي SoC الذين يرغبون في تنفيذ تحديثات نظام A/B التأكد من أن أداة تحميل التشغيل الخاصة بهم تنفذ Boot_control HAL وتمرير المعلمات الصحيحة إلى kernel.

تنفيذ التحكم في التمهيد HAL

يجب أن تقوم أدوات تحميل التشغيل التي تدعم A/B بتنفيذ boot_control HAL على hardware/libhardware/include/hardware/boot_control.h . يمكنك اختبار عمليات التنفيذ باستخدام الأداة المساعدة system/extras/bootctl و system/extras/tests/bootloader/ .

يجب عليك أيضًا تنفيذ آلة الحالة الموضحة أدناه:

الشكل 1. آلة حالة أداة تحميل التشغيل

قم بإعداد النواة

لتنفيذ تحديثات نظام A/B:

  1. اختر سلسلة تصحيحات kernel التالية (إذا لزم الأمر):
  2. تأكد من أن وسيطات سطر أوامر kernel تحتوي على الوسائط الإضافية التالية:
    skip_initramfs rootwait ro init=/init root="/dev/dm-0 dm=system none ro,0 1 android-verity <public-key-id> <path-to-system-partition>"
    ... حيث تكون قيمة <public-key-id> هي معرف المفتاح العام المستخدم للتحقق من توقيع جدول الحقيقة (لمزيد من التفاصيل، راجع dm-verity ) .
  3. أضف شهادة .X509 التي تحتوي على المفتاح العام إلى حلقة مفاتيح النظام:
    1. انسخ شهادة .X509 المنسقة بتنسيق .der إلى جذر دليل kernel . إذا تم تنسيق شهادة .X509 كملف .pem ، فاستخدم أمر openssl التالي للتحويل من تنسيق .pem إلى تنسيق .der :
      openssl x509 -in <x509-pem-certificate> -outform der -out <x509-der-certificate>
    2. أنشئ zImage لتضمين الشهادة كجزء من حلقة مفاتيح النظام. للتحقق، تحقق من إدخال procfs (يتطلب تمكين KEYS_CONFIG_DEBUG_PROC_KEYS ):
      angler:/# cat /proc/keys
      
      1c8a217e I------     1 perm 1f010000     0     0 asymmetri
      Android: 7e4333f9bba00adfe0ede979e28ed1920492b40f: X509.RSA 0492b40f []
      2d454e3e I------     1 perm 1f030000     0     0 keyring
      .system_keyring: 1/4
      يشير التضمين الناجح لشهادة .X509 إلى وجود المفتاح العام في حلقة مفاتيح النظام (يشير التمييز إلى معرف المفتاح العام).
    3. استبدل المسافة بـ # وقم بتمريرها كـ <public-key-id> في سطر أوامر kernel. على سبيل المثال، قم بتمرير Android:#7e4333f9bba00adfe0ede979e28ed1920492b40f بدلاً من <public-key-id> .

تعيين متغيرات البناء

يجب أن تستوفي أدوات تحميل التشغيل التي تدعم A/B معايير متغير البناء التالية:

يجب تحديد هدف A/B
  • AB_OTA_UPDATER := true
  • AB_OTA_PARTITIONS := \
    boot \
    system \
    vendor
    والأقسام الأخرى التي يتم تحديثها من خلال update_engine (الراديو، أداة تحميل التشغيل، وما إلى ذلك)
  • PRODUCT_PACKAGES += \
    update_engine \
    update_verifier
على سبيل المثال، راجع /device/google/marlin/+/android-7.1.0_r1/device-common.mk . يمكنك اختياريًا إجراء خطوة ما بعد التثبيت (لكن ما قبل إعادة التشغيل) dex2oat الموضحة في Compiling .
يوصى به بشدة لهدف A/B
  • حدد TARGET_NO_RECOVERY := true
  • حدد BOARD_USES_RECOVERY_AS_BOOT := true
  • لا تحدد BOARD_RECOVERYIMAGE_PARTITION_SIZE
لا يمكن تحديد هدف A/B
  • BOARD_CACHEIMAGE_PARTITION_SIZE
  • BOARD_CACHEIMAGE_FILE_SYSTEM_TYPE
اختياري لبنيات التصحيح PRODUCT_PACKAGES_DEBUG += update_engine_client

تعيين الأقسام (الفتحات)

لا تحتاج أجهزة A/B إلى قسم استرداد أو قسم ذاكرة تخزين مؤقت لأن Android لم يعد يستخدم هذه الأقسام. يتم الآن استخدام قسم البيانات لحزمة OTA التي تم تنزيلها، ويوجد رمز صورة الاسترداد على قسم التمهيد. يجب تسمية كافة الأقسام التي تكون A/B-ed على النحو التالي (يتم تسمية الفتحات دائمًا a و b وما إلى ذلك): boot_a و boot_b و system_a و system_b و vendor_a و vendor_b .

مخبأ

بالنسبة للتحديثات غير A/B، تم استخدام قسم ذاكرة التخزين المؤقت لتخزين حزم OTA التي تم تنزيلها ولإخفاء الكتل مؤقتًا أثناء تطبيق التحديثات. لم تكن هناك طريقة جيدة على الإطلاق لتحديد حجم قسم ذاكرة التخزين المؤقت: يعتمد الحجم المطلوب على التحديثات التي تريد تطبيقها. أسوأ الحالات هي أن يكون قسم ذاكرة التخزين المؤقت بحجم صورة النظام. مع تحديثات A/B ليست هناك حاجة لتخزين الكتل (لأنك تكتب دائمًا إلى قسم غير مستخدم حاليًا) ومع دفق A/B ليست هناك حاجة لتنزيل حزمة OTA بأكملها قبل تطبيقها.

استعادة

قرص ذاكرة الوصول العشوائي للاسترداد موجود الآن في ملف boot.img . عند الدخول في عملية الاسترداد، لا يمكن لمحمل التشغيل وضع خيار skip_initramfs في سطر أوامر kernel.

بالنسبة للتحديثات غير A/B، يحتوي قسم الاسترداد على الكود المستخدم لتطبيق التحديثات. يتم تطبيق تحديثات A/B بواسطة update_engine الذي يعمل في صورة النظام الذي تم تشغيله بشكل عادي. لا يزال هناك وضع استرداد يُستخدم لتنفيذ إعادة ضبط بيانات المصنع والتحميل الجانبي لحزم التحديث (ومن هنا جاء اسم "الاسترداد"). يتم تخزين التعليمات البرمجية والبيانات الخاصة بوضع الاسترداد في قسم التمهيد العادي في قرص ذاكرة الوصول العشوائي؛ للإقلاع في صورة النظام، يخبر محمل الإقلاع النواة بتخطي قرص ذاكرة الوصول العشوائي (وإلا فإن الجهاز يدخل في وضع الاسترداد. وضع الاسترداد صغير (وكان الكثير منه موجودًا بالفعل على قسم التمهيد)، لذا لا يزيد قسم التمهيد في الحجم.

فستاب

يجب أن تكون وسيطة slotselect على السطر لأقسام A/B-ed. على سبيل المثال:

<path-to-block-device>/vendor  /vendor  ext4  ro
wait,verify=<path-to-block-device>/metadata,slotselect

لا ينبغي تسمية أي قسم vendor . وبدلاً من ذلك، سيتم تحديد القسم vendor_a أو vendor_b وتثبيته على نقطة تحميل /vendor .

وسيطات فتحة Kernel

يجب تمرير لاحقة الفتحة الحالية إما من خلال عقدة شجرة جهاز معينة (DT) ( /firmware/android/slot_suffix ) أو من خلال سطر أوامر androidboot.slot_suffix kernel أو وسيطة bootconfig.

افتراضيًا، يقوم fastboot بوميض الفتحة الحالية على جهاز A/B. إذا كانت حزمة التحديث تحتوي أيضًا على صور للفتحة الأخرى غير الحالية، فسيقوم Fastboot بوميض تلك الصور أيضًا. تشمل الخيارات المتاحة ما يلي:

  • --slot SLOT . قم بتجاوز السلوك الافتراضي ومطالبة fastboot بوميض الفتحة التي تم تمريرها كوسيطة.
  • --set-active [ SLOT ] . اضبط الفتحة على أنها نشطة. إذا لم يتم تحديد وسيطة اختيارية، فسيتم تعيين الفتحة الحالية على أنها نشطة.
  • fastboot --help . الحصول على تفاصيل حول الأوامر.

إذا كان محمل الإقلاع يطبق fastboot، فيجب أن يدعم الأمر set_active <slot> الذي يضبط الفتحة النشطة الحالية على الفتحة المحددة (يجب أن يقوم هذا أيضًا بمسح علامة عدم التمهيد لتلك الفتحة وإعادة تعيين عدد مرات إعادة المحاولة إلى القيم الافتراضية). يجب أن يدعم برنامج تحميل التشغيل أيضًا المتغيرات التالية:

  • has-slot:<partition-base-name-without-suffix> . يُرجع "نعم" إذا كان القسم المحدد يدعم الفتحات، ويعيد "لا" بخلاف ذلك.
  • current-slot . إرجاع لاحقة الفتحة التي سيتم تمهيدها من التالي.
  • slot-count . إرجاع عدد صحيح يمثل عدد الفتحات المتاحة. حاليًا، يتم دعم فتحتين، لذا فإن هذه القيمة هي 2 .
  • slot-successful:<slot-suffix> . يتم إرجاع "نعم" إذا تم وضع علامة على الفتحة المحددة على أنها تم تشغيلها بنجاح، وإرجاع "لا" بخلاف ذلك.
  • slot-unbootable:<slot-suffix> . يتم إرجاع "نعم" إذا تم وضع علامة على الفتحة المحددة على أنها غير قابلة للتمهيد، وإرجاع "لا" بخلاف ذلك.
  • slot-retry-count . عدد مرات إعادة المحاولة المتبقية لمحاولة تشغيل الفتحة المحددة.

لعرض جميع المتغيرات، قم بتشغيل fastboot getvar all .

إنشاء حزم OTA

تتبع أدوات حزمة OTA نفس الأوامر مثل أوامر الأجهزة غير A/B. يجب إنشاء الملف target_files.zip عن طريق تحديد متغيرات البناء لهدف A/B. تقوم أدوات حزمة OTA تلقائيًا بتحديد وإنشاء الحزم بتنسيق مُحدِّث A/B.

أمثلة:

  • لإنشاء OTA كامل:
    ./build/make/tools/releasetools/ota_from_target_files \
        dist_output/tardis-target_files.zip \
        ota_update.zip
    
  • لإنشاء OTA تزايدي:
    ./build/make/tools/releasetools/ota_from_target_files \
        -i PREVIOUS-tardis-target_files.zip \
        dist_output/tardis-target_files.zip \
        incremental_ota_update.zip
    

تكوين الأقسام

يمكن لـ update_engine تحديث أي زوج من أقسام A/B المحددة في نفس القرص. يحتوي زوج الأقسام على بادئة مشتركة (مثل system أو boot ) ولاحقة لكل فتحة (مثل _a ). يتم تكوين قائمة الأقسام التي يحدد منشئ الحمولة النافعة تحديثًا لها بواسطة متغير التكوين AB_OTA_PARTITIONS .

على سبيل المثال، إذا تم تضمين زوج من الأقسام bootloader_a و booloader_b ( _a و _b هما لاحقتا الفتحة)، فيمكنك تحديث هذه الأقسام عن طريق تحديد ما يلي في تكوين المنتج أو اللوحة:

AB_OTA_PARTITIONS := \
  boot \
  system \
  bootloader

يجب ألا يتم تعديل جميع الأقسام التي تم تحديثها بواسطة update_engine بواسطة بقية النظام. أثناء التحديثات التزايدية أو تحديثات الدلتا ، يتم استخدام البيانات الثنائية من الفتحة الحالية لإنشاء البيانات في الفتحة الجديدة. قد يؤدي أي تعديل إلى فشل التحقق من بيانات الفتحة الجديدة أثناء عملية التحديث، وبالتالي فشل التحديث.

تكوين ما بعد التثبيت

يمكنك تكوين خطوة ما بعد التثبيت بشكل مختلف لكل قسم محدث باستخدام مجموعة من أزواج القيمة الرئيسية. لتشغيل برنامج موجود في /system/usr/bin/postinst في صورة جديدة، حدد المسار المتعلق بجذر نظام الملفات في قسم النظام.

على سبيل المثال، usr/bin/postinst هو system/usr/bin/postinst (إذا لم يكن يستخدم قرص RAM). بالإضافة إلى ذلك، حدد نوع نظام الملفات لتمريره إلى استدعاء النظام mount(2) . أضف ما يلي إلى ملفات .mk للمنتج أو الجهاز (إن أمكن):

AB_OTA_POSTINSTALL_CONFIG += \
  RUN_POSTINSTALL_system=true \
  POSTINSTALL_PATH_system=usr/bin/postinst \
  FILESYSTEM_TYPE_system=ext4

تجميع التطبيقات

يمكن تجميع التطبيقات في الخلفية قبل إعادة التشغيل باستخدام صورة النظام الجديدة. لتجميع التطبيقات في الخلفية، قم بإضافة ما يلي إلى تكوين جهاز المنتج (في Device.mk الخاص بالمنتج):

  1. قم بتضمين المكونات الأصلية في البناء لضمان تجميع البرنامج النصي للتجميع والثنائيات وإدراجها في صورة النظام.
      # A/B OTA dexopt package
      PRODUCT_PACKAGES += otapreopt_script
    
  2. قم بتوصيل البرنامج النصي للتجميع بـ update_engine بحيث يتم تشغيله كخطوة ما بعد التثبيت.
      # A/B OTA dexopt update_engine hookup
      AB_OTA_POSTINSTALL_CONFIG += \
        RUN_POSTINSTALL_system=true \
        POSTINSTALL_PATH_system=system/bin/otapreopt_script \
        FILESYSTEM_TYPE_system=ext4 \
        POSTINSTALL_OPTIONAL_system=true
    

للمساعدة في تثبيت الملفات المختارة مسبقًا في قسم النظام الثاني غير المستخدم، راجع تثبيت التمهيد الأول لملفات DEX_PREOPT .