A/B अपडेट लागू करना

जिन ओईएम और एसओसी वेंडर को A/B सिस्टम अपडेट लागू करने हैं उन्हें यह पक्का करना होगा कि उनके बूटलोडर में boot_control HAL लागू हो और वह कर्नेल को सही पैरामीटर पास करे.

बूट कंट्रोल HAL लागू करना

A/B अपडेट की सुविधा वाले बूटलोडर में, boot_control HAL को hardware/libhardware/include/hardware/boot_control.h पर लागू करना ज़रूरी है. यूटिलिटी और system/extras/tests/bootloader/ का इस्तेमाल करके, लागू करने की प्रोसेस की जांच की जा सकती है.system/extras/bootctl

आपको नीचे दिखाया गया स्टेट मशीन भी लागू करना होगा:

पहली इमेज. बूटलोडर स्टेट मशीन

कर्नेल सेट अप करना

A/B सिस्टम अपडेट लागू करने के लिए:

  1. ज़रूरत पड़ने पर, कर्नेल पैच की यह सीरीज़ चेरीपिक करें:
  2. पक्का करें कि कर्नेल कमांड लाइन आर्ग्युमेंट में ये अतिरिक्त आर्ग्युमेंट शामिल हों:
    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> वैल्यू, verity टेबल के हस्ताक्षर की पुष्टि करने के लिए इस्तेमाल की गई सार्वजनिक कुंजी का आईडी है. ज़्यादा जानकारी के लिए, 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> कर्नेल कमांड लाइन में पास करें. उदाहरण के लिए, <public-key-id> की जगह Android:#7e4333f9bba00adfe0ede979e28ed1920492b40f पास करें.

बिल्ड वैरिएबल सेट करना

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 अब इन पार्टिशन का इस्तेमाल नहीं करता अब डाउनलोड किए गए ओटीए पैकेज के लिए, डेटा पार्टिशन का इस्तेमाल किया जाता है. साथ ही, रिकवरी इमेज का कोड, बूट पार्टिशन पर होता है. A/B अपडेट की सुविधा वाले सभी पार्टिशन के नाम इस तरह होने चाहिए: स्लॉट के नाम हमेशा a, b, वगैरह होते हैं: boot_a, boot_b, system_a, system_b, vendor_a, vendor_b.

कैश मेमोरी

A/B अपडेट की सुविधा के बिना किए जाने वाले अपडेट के लिए, कैश मेमोरी पार्टिशन का इस्तेमाल, डाउनलोड किए गए ओटीए पैकेज को सेव करने और ब्लॉक को कुछ समय के लिए सेव करने के लिए किया जाता था. कैश मेमोरी पार्टिशन का साइज़ तय करने का कोई सही तरीका नहीं था. इसका साइज़, इस बात पर निर्भर करता था कि आपको कौनसे अपडेट लागू करने हैं. सबसे खराब स्थिति में, कैश मेमोरी पार्टिशन का साइज़, सिस्टम इमेज जितना हो सकता था. A/B अपडेट की सुविधा के साथ, ब्लॉक को सेव करने की ज़रूरत नहीं होती, क्योंकि हमेशा ऐसे पार्टिशन पर लिखा जाता है जिसका फ़िलहाल इस्तेमाल नहीं किया जा रहा है. साथ ही, स्ट्रीमिंग A/B अपडेट की सुविधा के साथ, ओटीए पैकेज को लागू करने से पहले, उसे डाउनलोड करने की ज़रूरत नहीं होती.

रिकवरी

रिकवरी रैम डिस्क अब boot.img फ़ाइल में शामिल है. रिकवरी में जाने पर, बूटलोडर नहीं डाल सकता है skip_initramfs विकल्प कर्नेल कमांड लाइन पर.

A/B अपडेट की सुविधा के बिना किए जाने वाले अपडेट के लिए, रिकवरी पार्टिशन में, अपडेट लागू करने के लिए इस्तेमाल किया गया कोड होता है. A/B अपडेट, नियमित रूप से बूट किए गए सिस्टम इमेज में चल रहे update_engine की मदद से लागू किए जाते हैं. फ़ैक्ट्री डेटा रीसेट करने और अपडेट पैकेज को साइडलोड करने के लिए, अब भी रिकवरी मोड का इस्तेमाल किया जाता है. "रिकवरी" नाम इसी वजह से पड़ा है. रिकवरी मोड के लिए कोड और डेटा, रैमडिस्क में नियमित बूट पार्टिशन में सेव किया जाता है. सिस्टम इमेज में बूट करने के लिए, बूटलोडर कर्नेल को रैमडिस्क को स्किप करने के लिए कहता है. ऐसा न करने पर, डिवाइस रिकवरी मोड में बूट हो जाता है. रिकवरी मोड का साइज़ छोटा होता है. साथ ही, इसका ज़्यादातर हिस्सा पहले से ही बूट पार्टिशन पर होता है. इसलिए, बूट पार्टिशन का साइज़ नहीं बढ़ता.

Fstab

A/B अपडेट की सुविधा वाले पार्टिशन की लाइन में, slotselect आर्ग्युमेंट ज़रूर होना चाहिए. उदाहरण के लिए:

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

किसी भी पार्टिशन का नाम vendor नहीं होना चाहिए. इसके बजाय, पार्टिशन vendor_a या vendor_b चुना जाएगा और इसे /vendor माउंट पॉइंट पर माउंट किया जाएगा.

कर्नेल स्लॉट आर्ग्युमेंट

मौजूदा स्लॉट सफ़िक्स को, किसी खास डिवाइस ट्री (डीटी) नोड (/firmware/android/slot_suffix) या androidboot.slot_suffix कर्नेल कमांड लाइन या बूटकॉन्फ़िग आर्ग्युमेंट के ज़रिए पास किया जाना चाहिए.

डिफ़ॉल्ट रूप से, 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:<slot-suffix>. दिए गए स्लॉट को बूट करने की कोशिश करने के लिए, बाकी बची कोशिशों की संख्या.

सभी वैरिएबल देखने के लिए, fastboot getvar all चलाएं.

ओटीए पैकेज जनरेट करना

ओटीए पैकेज के टूल, A/B अपडेट की सुविधा के बिना किए जाने वाले डिवाइसों के लिए इस्तेमाल किए जाने वाले कमांड के जैसे ही कमांड फ़ॉलो करते हैं. target_files.zip फ़ाइल, A/B टारगेट के लिए बिल्ड वैरिएबल तय करके जनरेट की जानी चाहिए. ओटीए पैकेज के टूल, A/B अपडेटर के फ़ॉर्मैट में पैकेज की पहचान करके उन्हें अपने-आप जनरेट करते हैं.

उदाहरण:

  • पूरा ओटीए जनरेट करने के लिए:
    ./build/make/tools/releasetools/ota_from_target_files \
        dist_output/tardis-target_files.zip \
        ota_update.zip
    
  • इंक्रीमेंटल ओटीए जनरेट करने के लिए:
    ./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 है. यह तब लागू होता है, जब रैम डिस्क का इस्तेमाल नहीं किया जा रहा हो. इसके अलावा, सिस्टम कॉल को पास करने के लिए, फ़ाइल सिस्टम का टाइप तय करें.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 फ़ाइलों को पहली बार बूट करने पर इंस्टॉल करना लेख पढ़ें.