تنفيذ تحديثات A / B

يجب على مصنعي المعدات الأصلية وبائعي 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. Cherrypick سلسلة تصحيح 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 . إذا تم تنسيق شهادة .pem كملف .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 بعد التثبيت (ولكن قبل إعادة التشغيل) الموضحة في الترجمة.
موصى به بشدة لهدف 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 بالكامل قبل تطبيقها.

استعادة

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

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

فستاب

يجب أن تكون وسيطة 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 mount point.

وسيطات فتحة Kernel

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

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

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

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

  • 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

تجميع

لأسباب أمنية ، لا يمكن system_server استخدام التجميع في الوقت المناسب (JIT) . هذا يعني أنه يجب عليك تجميع ملفات odex مسبقًا لـ system_server وتبعياتها كحد أدنى ؛ أي شيء آخر اختياري.

لتجميع التطبيقات في الخلفية ، يجب عليك إضافة ما يلي إلى تكوين جهاز المنتج (في 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 .