डिवाइस के लिए खास कोड

रिकवरी सिस्टम में, डिवाइस के हिसाब से कोड डालने के लिए कई हुक शामिल होते हैं.इससे, ओटीए अपडेट, Android सिस्टम के अलावा डिवाइस के अन्य हिस्सों को भी अपडेट कर सकते हैं. जैसे, बेसबैंड या रेडियो प्रोसेसर.

नीचे दिए गए सेक्शन और उदाहरणों में, yoyodyne वेंडर के बनाए गए tardis डिवाइस को पसंद के मुताबिक बनाया गया है.

पार्टीशन का मैप

Android 2.3 के बाद, यह प्लैटफ़ॉर्म eMMC फ़्लैश डिवाइसों और उन डिवाइसों पर काम करने वाले ext4 फ़ाइलसिस्टम के साथ काम करता है. यह मेमोरी टेक्नोलॉजी डिवाइस (MTD) फ़्लैश डिवाइसों और पुराने रिलीज़ के yaffs2 फ़ाइल सिस्टम के साथ भी काम करता है.

पार्टीशन मैप फ़ाइल को TARGET_RECOVERY_FSTAB से तय किया जाता है. इस फ़ाइल का इस्तेमाल, रिपेयर बाइनरी और पैकेज बनाने वाले टूल, दोनों करते हैं. BoardConfig.mk में, TARGET_RECOVERY_FSTAB में मैप फ़ाइल का नाम दिया जा सकता है.

पार्टीशन मैप की सैंपल फ़ाइल कुछ ऐसी दिख सकती है:

device/yoyodyne/tardis/recovery.fstab
# mount point       fstype  device       [device2]        [options (3.0+ only)]

/sdcard     vfat    /dev/block/mmcblk0p1 /dev/block/mmcblk0
/cache      yaffs2  cache
/misc       mtd misc
/boot       mtd boot
/recovery   emmc    /dev/block/platform/s3c-sdhci.0/by-name/recovery
/system     ext4    /dev/block/platform/s3c-sdhci.0/by-name/system length=-4096
/data       ext4    /dev/block/platform/s3c-sdhci.0/by-name/userdata

इस उदाहरण में, /sdcard को छोड़कर सभी माउंट पॉइंट तय किए जाने चाहिए. /sdcard को तय करना ज़रूरी नहीं है. डिवाइसों में अतिरिक्त पार्टीशन भी जोड़े जा सकते हैं. ये पांच फ़ाइल सिस्टम टाइप इस्तेमाल किए जा सकते हैं:

yaffs2
MTD फ़्लैश डिवाइस पर yaffs2 फ़ाइल सिस्टम. "device", MTD पार्टीशन का नाम होना चाहिए और यह /proc/mtd में दिखना चाहिए.
mtd
एक रॉ MTD पार्टीशन, जिसका इस्तेमाल बूट और रिकवरी जैसे बूट किए जा सकने वाले पार्टीशन के लिए किया जाता है. MTD को असल में mount नहीं किया जाता, लेकिन partition की जगह का पता लगाने के लिए, mount point का इस्तेमाल एक कुंजी के तौर पर किया जाता है. "device" के तौर पर, /proc/mtd में मौजूद MTD पार्टीशन का नाम होना चाहिए.
ext4
eMMC फ़्लैश डिवाइस पर ext4 फ़ाइलसिस्टम. "device", ब्लॉक किए गए डिवाइस का पाथ होना चाहिए.
emmc
रॉ eMMC ब्लॉक डिवाइस, जिसका इस्तेमाल बूट और रिकवरी जैसे बूट किए जा सकने वाले पार्टीशन के लिए किया जाता है. mtd टाइप की तरह ही, eMMC को कभी भी माउंट नहीं किया जाता. हालांकि, टेबल में डिवाइस की जगह का पता लगाने के लिए, माउंट पॉइंट स्ट्रिंग का इस्तेमाल किया जाता है.
vfat
ब्लॉक डिवाइस पर FAT फ़ाइल सिस्टम. आम तौर पर, यह एसडी कार्ड जैसे बाहरी स्टोरेज के लिए होता है. डिवाइस, ब्लॉक डिवाइस है; डिवाइस2 एक दूसरा ब्लॉक डिवाइस है. अगर मुख्य डिवाइस को माउंट करने में कोई गड़बड़ी होती है, तो सिस्टम इस डिवाइस को माउंट करने की कोशिश करता है. ऐसा, एसडी कार्ड के साथ काम करने के लिए किया जाता है. एसडी कार्ड, पार्टीशन टेबल के साथ फ़ॉर्मैट किए जा सकते हैं या नहीं भी.

सभी पार्टीशन, रूट डायरेक्ट्री में माउंट होने चाहिए. इसका मतलब है कि माउंट पॉइंट की वैल्यू, स्लैश से शुरू होनी चाहिए और उसमें कोई दूसरा स्लैश नहीं होना चाहिए. यह पाबंदी सिर्फ़ रिकवरी मोड में फ़ाइल सिस्टम को माउंट करने पर लागू होती है. मुख्य सिस्टम में, फ़ाइल सिस्टम को कहीं भी माउंट किया जा सकता है. डायरेक्ट्री /boot, /recovery, और /misc, रॉ टाइप (mtd या emmc) की होनी चाहिए. वहीं, डायरेक्ट्री /system, /data, /cache, और /sdcard (अगर उपलब्ध हो) फ़ाइल सिस्टम टाइप (yaffs2, ext4 या vfat) की होनी चाहिए.

Android 3.0 से, recovery.fstab फ़ाइल में एक और वैकल्पिक फ़ील्ड, options जुड़ा है. फ़िलहाल, लंबाई ही एक ऐसा विकल्प है जिसकी मदद से, विभाजन की लंबाई साफ़ तौर पर बताई जा सकती है. इस लंबाई का इस्तेमाल, partition को फिर से फ़ॉर्मैट करते समय किया जाता है. उदाहरण के लिए, डेटा मिटाने/फ़ैक्ट्री रीसेट करने के दौरान, userdata partition के लिए या पूरे OTA पैकेज को इंस्टॉल करते समय, system partition के लिए. अगर लंबाई की वैल्यू नेगेटिव है, तो फ़ॉर्मैट करने के लिए, लंबाई की वैल्यू को सही पार्टीशन साइज़ में जोड़कर साइज़ लिया जाता है. उदाहरण के लिए, "length=-16384" सेट करने का मतलब है कि उस पार्टीशन को फिर से फ़ॉर्मैट करने पर, उसके आखिरी 16k को ओवरराइट नहीं किया जाएगा. यह उपयोगकर्ता डेटा वाले partition को एन्क्रिप्ट (सुरक्षित) करने जैसी सुविधाओं के साथ काम करता है. इसमें, एन्क्रिप्शन मेटाडेटा को partition के आखिर में सेव किया जाता है, जिसे फिर से लिखा नहीं जाना चाहिए.

ध्यान दें: device2 और options फ़ील्ड की वैल्यू देना ज़रूरी नहीं है. इससे, पार्स करने में समस्या आ सकती है. अगर लाइन के चौथे फ़ील्ड में मौजूद एंट्री, ‘/' वर्ण से शुरू होती है, तो उसे device2 एंट्री माना जाता है. अगर एंट्री, ‘/' वर्ण से शुरू नहीं होती है, तो उसे options फ़ील्ड माना जाता है.

बूट ऐनिमेशन

डिवाइस बनाने वाली कंपनियों के पास, Android डिवाइस के चालू होने पर दिखने वाले ऐनिमेशन को पसंद के मुताबिक बनाने का विकल्प होता है. इसके लिए, बूटऐनिमेशन फ़ॉर्मैट में बताई गई खास बातों के मुताबिक, व्यवस्थित की गई और उसी जगह पर मौजूद .zip फ़ाइल बनाएं.

Android Things डिवाइसों के लिए, चुने गए प्रॉडक्ट में इमेज शामिल करने के लिए, Android Things कंसोल में ज़िप की गई फ़ाइल अपलोड की जा सकती है.

ध्यान दें: ये इमेज, Android ब्रैंड के दिशा-निर्देशों के मुताबिक होनी चाहिए. ब्रैंड के दिशा-निर्देशों के लिए, Partner Marketing Hub के Android सेक्शन पर जाएं.

रिकवरी यूज़र इंटरफ़ेस (यूआई)

अलग-अलग हार्डवेयर (फ़िज़िकल बटन, एलईडी, स्क्रीन वगैरह) वाले डिवाइसों के साथ काम करने के लिए, रिकवरी इंटरफ़ेस को अपनी पसंद के मुताबिक बनाया जा सकता है, ताकि स्टेटस दिखाया जा सके. साथ ही, हर डिवाइस के लिए मैन्युअल तरीके से चालू की जाने वाली, छिपी हुई सुविधाओं को ऐक्सेस किया जा सके.

आपका लक्ष्य, डिवाइस के हिसाब से काम करने वाली सुविधा देने के लिए, कुछ C++ ऑब्जेक्ट के साथ एक छोटी स्टैटिक लाइब्रेरी बनाना है. फ़ाइल bootable/recovery/default_device.cpp का इस्तेमाल डिफ़ॉल्ट रूप से किया जाता है. साथ ही, अपने डिवाइस के लिए इस फ़ाइल का वर्शन लिखते समय, कॉपी करने के लिए यह एक अच्छा शुरुआती पॉइंट है.

ध्यान दें: आपको यहां कोई निर्देश नहीं है मैसेज दिख सकता है. टेक्स्ट को टॉगल करने के लिए, आवाज़ तेज़ करने वाले बटन को दबाते समय पावर बटन को दबाकर रखें. अगर आपके डिवाइस में दोनों बटन नहीं हैं, तो टेक्स्ट को टॉगल करने के लिए, किसी भी बटन को दबाकर रखें.

device/yoyodyne/tardis/recovery/recovery_ui.cpp
#include <linux/input.h>

#include "common.h"
#include "device.h"
#include "screen_ui.h"

हेडर और आइटम फ़ंक्शन

डिवाइस क्लास में, हेडर और आइटम दिखाने के लिए फ़ंक्शन की ज़रूरत होती है. ये हेडर और आइटम, छिपे हुए रिकवरी मेन्यू में दिखते हैं. हेडर से मेन्यू को इस्तेमाल करने का तरीका पता चलता है. जैसे, हाइलाइट किए गए आइटम को बदलने/चुने जाने के लिए कंट्रोल.

static const char* HEADERS[] = { "Volume up/down to move highlight;",
                                 "power button to select.",
                                 "",
                                 NULL };

static const char* ITEMS[] =  {"reboot system now",
                               "apply update from ADB",
                               "wipe data/factory reset",
                               "wipe cache partition",
                               NULL };

ध्यान दें: लंबी लाइनें छोटी कर दी जाती हैं (रैप नहीं की जातीं). इसलिए, अपने डिवाइस की स्क्रीन की चौड़ाई को ध्यान में रखें.

CheckKey को पसंद के मुताबिक बनाना

इसके बाद, अपने डिवाइस पर RecoveryUI लागू करने का तरीका तय करें. इस उदाहरण में यह माना गया है कि tardis डिवाइस में स्क्रीन है, ताकि आप डिवाइस में पहले से मौजूद ScreenRecoveryUIimplementation को इनहेरिट कर सकें. बिना स्क्रीन वाले डिवाइसों के लिए निर्देश देखें. ScreenRecoveryUI में, CheckKey() को पसंद के मुताबिक बनाया जा सकता है. यह फ़ंक्शन, शुरू में, एक साथ काम न करने वाली बटन को मैनेज करता है:

class TardisUI : public ScreenRecoveryUI {
  public:
    virtual KeyAction CheckKey(int key) {
        if (key == KEY_HOME) {
            return TOGGLE;
        }
        return ENQUEUE;
    }
};

मुख्य कॉन्स्टेंट

KEY_* कॉन्स्टेंट की परिभाषा linux/input.h में दी गई है. CheckKey() को रिपेयर के बाकी हिस्से में होने वाली किसी भी स्थिति में कॉल किया जाता है: जब मेन्यू को टॉगल किया गया हो, जब वह चालू हो, पैकेज इंस्टॉल करने के दौरान, उपयोगकर्ता डेटा मिटाने के दौरान वगैरह. यह चार में से किसी एक कॉन्स्टेंट को दिखा सकता है:

  • TOGGLE. मेन्यू और/या टेक्स्ट लॉग को टॉगल करके, उसे चालू या बंद करना
  • रीबूट करें. डिवाइस को तुरंत रीस्टार्ट करें
  • IGNORE. इस बटन को अनदेखा करें
  • ENQUEUE. इस कीबोर्ड प्रेस को सिंक्रोनस तरीके से इस्तेमाल करने के लिए, सूची में जोड़ें. उदाहरण के लिए, अगर डिसप्ले चालू है, तो रिकवरी मेन्यू सिस्टम

CheckKey() को हर बार तब कॉल किया जाता है, जब किसी बटन को दबाने के बाद, उसी बटन को छोड़ने पर, बटन दबाने का इवेंट और बटन छोड़ने का इवेंट, दोनों रिकॉर्ड किए जाते हैं. (इवेंट के क्रम A-डाउन B-डाउन B-अप A-अप के नतीजे में सिर्फ़ CheckKey(B) को कॉल किया जाता है.) CheckKey() , IsKeyPressed() को कॉल करके यह पता लगा सकता है कि कोई दूसरी बटन दबा हुआ है या नहीं. (ऊपर दिए गए मुख्य इवेंट के क्रम में, अगर CheckKey(B) ने IsKeyPressed(A) को कॉल किया होता, तो यह सही के तौर पर दिखता.)

CheckKey() अपनी क्लास में स्टेटस बनाए रख सकता है; यह कुंजियों के क्रम का पता लगाने के लिए मददगार हो सकता है. इस उदाहरण में, थोड़ा ज़्यादा जटिल सेटअप दिखाया गया है: डिसप्ले को टॉगल करने के लिए, पावर बटन को दबाकर रखें और आवाज़ बढ़ाने वाले बटन को दबाएं. साथ ही, डिवाइस को तुरंत रीबूट करने के लिए, पावर बटन को लगातार पांच बार दबाएं. इस दौरान, किसी अन्य बटन को न दबाएं:

class TardisUI : public ScreenRecoveryUI {
  private:
    int consecutive_power_keys;

  public:
    TardisUI() : consecutive_power_keys(0) {}

    virtual KeyAction CheckKey(int key) {
        if (IsKeyPressed(KEY_POWER) && key == KEY_VOLUMEUP) {
            return TOGGLE;
        }
        if (key == KEY_POWER) {
            ++consecutive_power_keys;
            if (consecutive_power_keys >= 5) {
                return REBOOT;
            }
        } else {
            consecutive_power_keys = 0;
        }
        return ENQUEUE;
    }
};

ScreenRecoveryUI

ScreenRecoveryUI के साथ अपनी इमेज (गड़बड़ी का आइकॉन, इंस्टॉलेशन ऐनिमेशन, प्रोग्रेस बार) का इस्तेमाल करते समय, वैरिएबल animation_fps सेट करके, ऐनिमेशन की फ़्रेम प्रति सेकंड (एफ़पीएस) की स्पीड को कंट्रोल किया जा सकता है.

ध्यान दें: मौजूदा interlace-frames.py स्क्रिप्ट की मदद से, इमेज में ही animation_fps की जानकारी को स्टोर किया जा सकता है. Android के पुराने वर्शन में, animation_fps को खुद से सेट करना ज़रूरी था.

वैरिएबल animation_fps को सेट करने के लिए, अपनी सबक्लास में ScreenRecoveryUI::Init() फ़ंक्शन को बदलें. वैल्यू सेट करें. इसके बाद, शुरू करने की प्रोसेस पूरी करने के लिए parent Init() फ़ंक्शन को कॉल करें. डिफ़ॉल्ट वैल्यू (20 FPS) डिफ़ॉल्ट रिकवरी इमेज से मेल खाती है. इन इमेज का इस्तेमाल करते समय, आपको Init() फ़ंक्शन देने की ज़रूरत नहीं होती. इमेज के बारे में जानकारी के लिए, रिकवरी यूज़र इंटरफ़ेस (यूआई) की इमेज देखें.

डिवाइस क्लास

RecoveryUI लागू करने के बाद, अपनी डिवाइस क्लास तय करें. यह डिवाइस की डिफ़ॉल्ट क्लास की सबसे छोटी क्लास होती है. यह आपकी यूज़र इंटरफ़ेस (यूआई) क्लास का एक इंस्टेंस बनाकर, उसे GetUI() फ़ंक्शन से रिटर्न करना चाहिए:

class TardisDevice : public Device {
  private:
    TardisUI* ui;

  public:
    TardisDevice() :
        ui(new TardisUI) {
    }

    RecoveryUI* GetUI() { return ui; }

StartRecovery

StartRecovery() तरीके को रिकवरी शुरू होने पर, यूज़र इंटरफ़ेस (यूआई) को शुरू करने और आर्ग्युमेंट को पार्स करने के बाद, लेकिन कोई कार्रवाई करने से पहले कॉल किया जाता है. डिफ़ॉल्ट रूप से लागू करने पर, कुछ नहीं होता. इसलिए, अगर आपको कुछ नहीं करना है, तो आपको अपने सबक्लास में इसे देने की ज़रूरत नहीं है:

   void StartRecovery() {
       // ... do something tardis-specific here, if needed ....
    }

रिकवरी मेन्यू की सुविधा उपलब्ध कराना और उसे मैनेज करना

हेडर लाइन और आइटम की सूची पाने के लिए, सिस्टम दो तरीकों का इस्तेमाल करता है. इस लागू करने के तरीके में, यह फ़ाइल के सबसे ऊपर तय किए गए स्टैटिक ऐरे दिखाता है:

const char* const* GetMenuHeaders() { return HEADERS; }
const char* const* GetMenuItems() { return ITEMS; }

HandleMenuKey

इसके बाद, HandleMenuKey() फ़ंक्शन दें. यह फ़ंक्शन, कीबोर्ड पर किसी बटन को दबाने और मेन्यू के दिखने की मौजूदा स्थिति के आधार पर यह तय करता है कि क्या कार्रवाई की जाए:

   int HandleMenuKey(int key, int visible) {
        if (visible) {
            switch (key) {
              case KEY_VOLUMEDOWN: return kHighlightDown;
              case KEY_VOLUMEUP:   return kHighlightUp;
              case KEY_POWER:      return kInvokeItem;
            }
        }
        return kNoAction;
    }

यह तरीका, एक कुंजी कोड (जिसे पहले यूज़र इंटरफ़ेस ऑब्जेक्ट के CheckKey() तरीके से प्रोसेस और सूची में जोड़ा गया है) और मेन्यू/टेक्स्ट लॉग के दिखने की मौजूदा स्थिति लेता है. रिटर्न वैल्यू एक पूर्णांक होती है. अगर वैल्यू 0 या उससे ज़्यादा है, तो उसे मेन्यू आइटम की वैल्यू माना जाता है. इसे तुरंत लागू किया जाता है. इसके लिए, यहां दिया गया InvokeMenuItem() तरीका देखें. अगर ऐसा नहीं है, तो यह इनमें से कोई एक पहले से तय की गई वैल्यू हो सकती है:

  • kHighlightUp. मेन्यू हाइलाइट को पिछले आइटम पर ले जाना
  • kHighlightDown. मेन्यू हाइलाइट को अगले आइटम पर ले जाना
  • kInvokeItem. हाइलाइट किए गए मौजूदा आइटम को चालू करना
  • kNoAction. इस बटन को दबाने पर कुछ न करें

दिखने वाले आर्ग्युमेंट से पता चलता है कि मेन्यू न दिखने पर भी HandleMenuKey() को कॉल किया जाता है. CheckKey() के उलट, रिकवरी के दौरान इसे नहीं बुलाया जाता. जैसे, डेटा मिटाना या पैकेज इंस्टॉल करना. इसे सिर्फ़ तब बुलाया जाता है, जब रिकवरी में कोई गतिविधि न हो और वह इनपुट का इंतज़ार कर रही हो.

ट्रैकबॉल के काम करने का तरीका

अगर आपके डिवाइस में ट्रैकबॉल जैसा इनपुट मैकेनिज्म है (यह EV_REL और कोड REL_Y टाइप के इनपुट इवेंट जनरेट करता है), तो रिकवरी मोड में, जब भी ट्रैकबॉल जैसा इनपुट डिवाइस Y ऐक्सिस में मोशन की जानकारी देता है, तो KEY_UP और KEY_DOWN बटन दबाने की गतिविधि जनरेट होती है. इसके लिए, आपको KEY_UP और KEY_DOWN इवेंट को मेन्यू ऐक्शन पर मैप करना होगा. CheckKey() के लिए यह मैपिंग नहीं होती. इसलिए, डिसप्ले को रीबूट करने या टॉगल करने के लिए, ट्रैकबॉल मोशन को ट्रिगर के तौर पर इस्तेमाल नहीं किया जा सकता.

कार्रवाई बदलने वाली कुंजियां

यह देखने के लिए कि कौनसी कुंजियां कार्रवाई बदलने वाली कुंजियों के तौर पर दबाकर रखी गई हैं, अपने यूज़र इंटरफ़ेस ऑब्जेक्ट का IsKeyPressed() तरीका कॉल करें. उदाहरण के लिए, कुछ डिवाइसों पर रिकवरी मोड में Alt-W दबाने से, डेटा वाइप होने की प्रोसेस शुरू हो जाएगी. भले ही, मेन्यू दिख रहा हो या नहीं. इसे इस तरह लागू किया जा सकता है:

   int HandleMenuKey(int key, int visible) {
        if (ui->IsKeyPressed(KEY_LEFTALT) && key == KEY_W) {
            return 2;  // position of the "wipe data" item in the menu
        }
        ...
    }

ध्यान दें: अगर visible की वैल्यू गलत है, तो मेन्यू में बदलाव करने वाली खास वैल्यू (हाइलाइट को एक जगह से दूसरी जगह ले जाना, हाइलाइट किए गए आइटम को चालू करना) दिखाने का कोई मतलब नहीं है. ऐसा इसलिए है, क्योंकि उपयोगकर्ता को हाइलाइट नहीं दिखती. हालांकि, आपके पास वैल्यू दिखाने का विकल्प होता है.

InvokeMenuItem

इसके बाद, InvokeMenuItem() का ऐसा तरीका दें जो GetMenuItems() से मिले आइटम के कलेक्शन में मौजूद पूर्णांक की पोज़िशन को कार्रवाइयों पर मैप करता हो. tardis उदाहरण में आइटम के कलेक्शन के लिए, इनका इस्तेमाल करें:

   BuiltinAction InvokeMenuItem(int menu_position) {
        switch (menu_position) {
          case 0: return REBOOT;
          case 1: return APPLY_ADB_SIDELOAD;
          case 2: return WIPE_DATA;
          case 3: return WIPE_CACHE;
          default: return NO_ACTION;
        }
    }

यह तरीका, BuiltinAction एनम के किसी भी सदस्य को दिखा सकता है, ताकि सिस्टम को वह कार्रवाई करने के लिए कहा जा सके. अगर आपको सिस्टम को कुछ नहीं करना है, तो NO_ACTION सदस्य को दिखाएं. यहां पर, सिस्टम में मौजूद रिकवरी फ़ंक्शन के अलावा, रिकवरी के लिए और भी फ़ंक्शन जोड़े जा सकते हैं: इसके लिए, अपने मेन्यू में एक आइटम जोड़ें. जब उस मेन्यू आइटम को चालू किया जाए, तो उसे यहां पर चलाएं और NO_ACTION दिखाएं, ताकि सिस्टम कुछ और न करे.

BuiltinAction में ये वैल्यू शामिल हैं:

  • NO_ACTION. कुछ न करें.
  • रीबूट करें. रिकवरी मोड से बाहर निकलें और डिवाइस को सामान्य तरीके से रीबूट करें.
  • APPLY_EXT, APPLY_CACHE, APPLY_ADB_SIDELOAD. अलग-अलग जगहों से अपडेट पैकेज इंस्टॉल करना. ज़्यादा जानकारी के लिए, साइडलोड करना लेख पढ़ें.
  • WIPE_CACHE. सिर्फ़ कैश मेमोरी वाले पार्टिशन को फिर से फ़ॉर्मैट करें. पुष्टि करने की ज़रूरत नहीं है, क्योंकि यह अपेक्षाकृत नुकसानदेह नहीं है.
  • WIPE_DATA. उपयोगकर्ता डेटा और कैश मेमोरी के पार्टिशन को फिर से फ़ॉर्मैट करना. इसे फ़ैक्ट्री डेटा रीसेट भी कहा जाता है. आगे बढ़ने से पहले, उपयोगकर्ता से इस कार्रवाई की पुष्टि करने के लिए कहा जाता है.

आखिरी तरीका, WipeData(), ज़रूरी नहीं है. इसे तब कॉल किया जाता है, जब डेटा मिटाने की प्रोसेस शुरू की जाती है. यह प्रोसेस, मेन्यू के ज़रिए रिकवरी से या फिर जब उपयोगकर्ता मुख्य सिस्टम से फ़ैक्ट्री डेटा रीसेट करने का विकल्प चुनता है, तब शुरू की जाती है. उपयोगकर्ता के डेटा और कैश मेमोरी के सेगमेंट मिटाने से पहले, इस तरीके को कॉल किया जाता है. अगर आपके डिवाइस में उपयोगकर्ता का डेटा, इन दोनों सेटिंग के अलावा किसी और जगह सेव किया गया है, तो आपको यहां जाकर उसे मिटाना चाहिए. आपको सफलता का पता चलने पर 0 और गड़बड़ी का पता चलने पर कोई दूसरी वैल्यू दिखानी चाहिए. हालांकि, फ़िलहाल रिटर्न वैल्यू को अनदेखा किया जाता है. उपयोगकर्ता का डेटा और कैश मेमोरी के सेक्शन मिटा दिए जाते हैं. भले ही, आपने सफलता या गड़बड़ी की जानकारी दी हो.

   int WipeData() {
       // ... do something tardis-specific here, if needed ....
       return 0;
    }

डिवाइस बनाने वाली कंपनी

आखिर में, recovery_ui.cpp फ़ाइल के आखिर में, make_device() फ़ंक्शन के लिए कुछ बोइलरप्लेट शामिल करें. यह फ़ंक्शन, आपकी डिवाइस क्लास का एक इंस्टेंस बनाता है और उसे दिखाता है:

class TardisDevice : public Device {
   // ... all the above methods ...
};

Device* make_device() {
    return new TardisDevice();
}

recovery_ui.cpp फ़ाइल को पूरा करने के बाद, उसे बनाएं और अपने डिवाइस पर रिकवरी से लिंक करें. Android.mk में, एक स्टैटिक लाइब्रेरी बनाएं जिसमें सिर्फ़ यह C++ फ़ाइल शामिल हो:

device/yoyodyne/tardis/recovery/Android.mk
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)

LOCAL_MODULE_TAGS := eng
LOCAL_C_INCLUDES += bootable/recovery
LOCAL_SRC_FILES := recovery_ui.cpp

# should match TARGET_RECOVERY_UI_LIB set in BoardConfig.mk
LOCAL_MODULE := librecovery_ui_tardis

include $(BUILD_STATIC_LIBRARY)

इसके बाद, इस डिवाइस के बोर्ड कॉन्फ़िगरेशन में, अपनी स्टैटिक लाइब्रेरी को TARGET_RECOVERY_UI_LIB की वैल्यू के तौर पर बताएं.

device/yoyodyne/tardis/BoardConfig.mk
 [...]

# device-specific extensions to the recovery UI
TARGET_RECOVERY_UI_LIB := librecovery_ui_tardis

रिकवरी यूज़र इंटरफ़ेस (यूआई) की इमेज

रिकवरी के यूज़र इंटरफ़ेस में इमेज होती हैं. आम तौर पर, उपयोगकर्ता यूज़र इंटरफ़ेस के साथ कभी इंटरैक्ट नहीं करते: सामान्य अपडेट के दौरान, फ़ोन रिकवरी मोड में बूट होता है, इंस्टॉलेशन की प्रोग्रेस बार भरता है, और उपयोगकर्ता के इनपुट के बिना नए सिस्टम में बूट होता है. सिस्टम अपडेट से जुड़ी समस्या होने पर, उपयोगकर्ता के पास सिर्फ़ ग्राहक सेवा केंद्र को कॉल करने का विकल्प होता है.

सिर्फ़ इमेज वाले इंटरफ़ेस की मदद से, स्थानीय भाषा में अनुवाद करने की ज़रूरत नहीं होती. हालांकि, Android 5.0 के बाद, अपडेट के दौरान इमेज के साथ टेक्स्ट की स्ट्रिंग भी दिख सकती है. जैसे, "सिस्टम अपडेट इंस्टॉल किया जा रहा है...". ज़्यादा जानकारी के लिए, स्थानीय भाषा में लिखा गया रिकवरी टेक्स्ट लेख पढ़ें.

Android 5.0 और उसके बाद के वर्शन

Android 5.0 और उसके बाद के वर्शन के रिकवरी यूज़र इंटरफ़ेस में, दो मुख्य इमेज का इस्तेमाल किया जाता है: गड़बड़ी वाली इमेज और इंस्टॉल हो रहा है ऐनिमेशन.

ओटीए से जुड़ी गड़बड़ी के दौरान दिखाई गई इमेज

पहली इमेज. icon_error.png

ओटीए इंस्टॉल के दौरान दिखाई गई इमेज

दूसरी इमेज. icon_installing.png

इंस्टॉल करने के दौरान दिखने वाले ऐनिमेशन को एक PNG इमेज के तौर पर दिखाया जाता है. इसमें ऐनिमेशन के अलग-अलग फ़्रेम, पंक्ति के हिसाब से इंटरलेस किए जाते हैं. इस वजह से, दूसरा फ़ोटो छोटा दिखता है. उदाहरण के लिए, अगर आपको एक ऐसा ऐनिमेशन बनाना है जिसमें सात फ़्रेम हों और हर फ़्रेम का डाइमेंशन 200x200 हो, तो एक ऐसी इमेज बनाएं जिसका डाइमेंशन 200x1400 हो. इसमें पहला फ़्रेम, पंक्तियां 0, 7, 14, 21 वगैरह होंगी. दूसरा फ़्रेम, पंक्तियां 1, 8, 15, 22 वगैरह होंगी. इसी तरह, बाकी फ़्रेम भी बनाएं. इस इमेज में एक टेक्स्ट चंक भी शामिल होता है. इससे ऐनिमेशन फ़्रेम की संख्या और हर सेकंड में दिखने वाले फ़्रेम की संख्या (एफ़पीएस) का पता चलता है. टूल bootable/recovery/interlace-frames.py, इनपुट फ़्रेम का एक सेट लेता है और उन्हें रिकवरी के लिए इस्तेमाल की जाने वाली ज़रूरी कंपोज़िट इमेज में जोड़ता है.

डिफ़ॉल्ट इमेज अलग-अलग डेंसिटी में उपलब्ध होती हैं और ये bootable/recovery/res-$DENSITY/images में होती हैं (उदाहरण के लिए, bootable/recovery/res-hdpi/images). इंस्टॉलेशन के दौरान स्टैटिक इमेज का इस्तेमाल करने के लिए, आपको सिर्फ़ icon_installing.png इमेज देनी होगी और ऐनिमेशन में फ़्रेम की संख्या को 0 पर सेट करना होगा. गड़बड़ी के आइकॉन में ऐनिमेशन नहीं होता है, यह हमेशा स्टैटिक इमेज होती है.

Android 4.x और उससे पहले के वर्शन

Android 4.x और उससे पहले के वर्शन के रिकवरी यूज़र इंटरफ़ेस (यूआई) में, गड़बड़ी वाली इमेज (ऊपर दिखाई गई) और इंस्टॉल हो रहा है ऐनिमेशन के साथ-साथ कई ओवरले इमेज का इस्तेमाल किया जाता है:

ओटीए इंस्टॉल के दौरान दिखाई गई इमेज

तीसरी इमेज. icon_installing.png

पहली ओवरले के तौर पर दिखाई गई इमेज

चौथी इमेज. icon-installing_overlay01.png

सातवें ओवरले के तौर पर दिखाई गई इमेज

पांचवीं इमेज. icon_installing_overlay07.png

इंस्टॉलेशन के दौरान, स्क्रीन पर दिखने वाला डिसप्ले, icon_installing.png इमेज को ड्रॉ करके बनाया जाता है. इसके बाद, सही ऑफ़सेट पर उसके ऊपर ओवरले फ़्रेम में से कोई एक ड्रॉ किया जाता है. यहां, ओवरले को बेस इमेज के ऊपर कहां रखा गया है, यह हाइलाइट करने के लिए लाल रंग का एक बॉक्स सुपरइंपोज़ किया गया है:

इंस्टॉल प्लस के पहले ओवरले की कंपोजिट इमेज

छठी इमेज. ऐनिमेशन फ़्रेम 1 इंस्टॉल किया जा रहा है (icon_installing.png + icon_installing_overlay01.png)

इंस्टॉल और सातवें ओवरले की कंपोजिट इमेज

सातवीं इमेज. ऐनिमेशन फ़्रेम 7 इंस्टॉल किया जा रहा है (icon_installing.png + icon_installing_overlay07.png)

अगले फ़्रेम, पहले से मौजूद फ़्रेम के ऊपर सिर्फ़ अगली ओवरले इमेज को ड्रॉ करके दिखाए जाते हैं. इसमें, बेस इमेज को फिर से नहीं बनाया जाता.

ऐनिमेशन में फ़्रेम की संख्या, पसंदीदा स्पीड, और ओवरले के x- और y-ऑफ़सेट को, ScreenRecoveryUI क्लास के सदस्य वैरिएबल से सेट किया जाता है. डिफ़ॉल्ट इमेज के बजाय, कस्टम इमेज का इस्तेमाल करते समय, अपनी कस्टम इमेज के लिए इन वैल्यू को बदलने के लिए, अपने सबक्लास में Init() तरीके को बदलें. ज़्यादा जानकारी के लिए, ScreenRecoveryUI देखें. स्क्रिप्ट bootable/recovery/make-overlay.py , इमेज फ़्रेम के सेट को "बेस इमेज + ओवरले इमेज" फ़ॉर्मैट में बदलने में मदद कर सकती है. यह फ़ॉर्मैट, रिकवरी के लिए ज़रूरी है. इसमें ज़रूरी ऑफ़सेट का हिसाब लगाना भी शामिल है.

डिफ़ॉल्ट इमेज bootable/recovery/res/images में मौजूद होती हैं. इंस्टॉलेशन के दौरान स्टैटिक इमेज का इस्तेमाल करने के लिए, आपको सिर्फ़ icon_installing.png इमेज देनी होगी और ऐनिमेशन में फ़्रेम की संख्या को 0 पर सेट करना होगा. गड़बड़ी वाले आइकॉन में ऐनिमेशन नहीं होता है. यह हमेशा स्टैटिक इमेज होती है.

स्थानीय भाषा में लिखा गया, खाता वापस पाने का अनुरोध

Android 5.x में टेक्स्ट की एक स्ट्रिंग दिखती है (उदाहरण के लिए, "सिस्टम अपडेट इंस्टॉल किया जा रहा है...") के साथ-साथ इमेज भी दिखेगी. जब मुख्य सिस्टम रिकवरी मोड में बूट होता है, तो वह रिकवरी मोड में, उपयोगकर्ता की मौजूदा स्थानीय भाषा को कमांड-लाइन विकल्प के तौर पर पास करता है. दिखाए जाने वाले हर मैसेज के लिए, रिकवरी में एक दूसरी कंपोजिट इमेज शामिल होती है. इसमें हर स्थानीय भाषा में, उस मैसेज के लिए पहले से रेंडर की गई टेक्स्ट स्ट्रिंग होती हैं.

रिकवरी टेक्स्ट स्ट्रिंग की सैंपल इमेज:

रिकवरी टेक्स्ट की इमेज

आठवीं इमेज. खाता वापस पाने के मैसेज के लिए स्थानीय भाषा में टेक्स्ट

रिकवरी टेक्स्ट में ये मैसेज दिख सकते हैं:

  • सिस्टम अपडेट इंस्टॉल किया जा रहा है...
  • गड़बड़ी!
  • मिटाया जा रहा है... (डेटा मिटाने/फ़ैक्ट्री रीसेट करने पर)
  • कोई निर्देश नहीं (जब कोई उपयोगकर्ता मैन्युअल तरीके से रिकवरी मोड में बूट करता है)

bootable/recovery/tools/recovery_l10n/ में मौजूद Android ऐप्लिकेशन, मैसेज के स्थानीय भाषा में अनुवाद को रेंडर करता है और कॉम्पोज़िट इमेज बनाता है. इस ऐप्लिकेशन को इस्तेमाल करने के बारे में ज़्यादा जानने के लिए, bootable/recovery/tools/recovery_l10n/src/com/android/recovery_l10n/Main.java में दी गई टिप्पणियां देखें.

जब कोई उपयोगकर्ता मैन्युअल तरीके से रिकवरी मोड में बूट करता है, तो हो सकता है कि स्थानीय भाषा उपलब्ध न हो और कोई टेक्स्ट न दिखे. मैसेज को, खाता वापस पाने की प्रोसेस के लिए ज़रूरी न बनाएं.

ध्यान दें: लॉग मैसेज दिखाने वाला और उपयोगकर्ता को मेन्यू से कार्रवाइयां चुनने की अनुमति देने वाला छिपा इंटरफ़ेस, सिर्फ़ अंग्रेज़ी में उपलब्ध है.

प्रोग्रेस बार

प्रोग्रेस बार, मुख्य इमेज (या ऐनिमेशन) के नीचे दिख सकते हैं. प्रोग्रेस बार बनाने के लिए, दो इनपुट इमेज को जोड़ा जाता है. ये इमेज एक ही साइज़ की होनी चाहिए:

खाली प्रोग्रेस बार

नौवीं इमेज. progress_empty.png

पूरा प्रोग्रेस बार

10वीं इमेज. progress_fill.png

प्रोग्रेस बार बनाने के लिए, भरी हुई इमेज के बाएं हिस्से को, खाली इमेज के दाएं हिस्से के बगल में दिखाया जाता है. प्रोग्रेस दिखाने के लिए, दो इमेज के बीच की बाउंड्री की पोज़िशन बदल दी जाती है. उदाहरण के लिए, इनपुट इमेज के ऊपर दिए गए पेयर के साथ, यह दिखाएं:

प्रोग्रेस बार 1% पर है

11वीं इमेज. प्रोग्रेस बार 1% पर है>

प्रोग्रेस बार 10% पर है

12वीं इमेज. प्रोग्रेस बार 10% पर है

प्रोग्रेस बार 50% पर है

13वीं इमेज. प्रोग्रेस बार 50% पर है

इन इमेज के डिवाइस के हिसाब से वर्शन दिए जा सकते हैं. इसके लिए, इन्हें device/yoyodyne/tardis/recovery/res/images में डालें (इस उदाहरण में) . फ़ाइलों के नाम, ऊपर दी गई सूची में शामिल नामों से मेल खाने चाहिए. जब उस डायरेक्ट्री में कोई फ़ाइल मिलती है, तो बिल्ड सिस्टम उससे जुड़ी डिफ़ॉल्ट इमेज के बजाय उसका इस्तेमाल करता है. सिर्फ़ आरजीबी या 8-बिट कलर डेप्थ वाले RGBA फ़ॉर्मैट में PNG फ़ाइलें अपलोड की जा सकती हैं.

ध्यान दें: Android 5.x में, अगर स्थानीय भाषा को रिकवरी के लिए सेट किया गया है और वह दाईं से बाईं ओर (आरटीएल) लिखी जाने वाली भाषा (अरेबिक, हिब्रू वगैरह) है, तो प्रोग्रेस बार दाईं से बाईं ओर भरता है.

बिना स्क्रीन वाले डिवाइस

सभी Android डिवाइसों में स्क्रीन नहीं होती. अगर आपका डिवाइस हेडलेस ऐप्लिकेशन है या उसमें सिर्फ़ ऑडियो इंटरफ़ेस है, तो आपको रिकवरी यूज़र इंटरफ़ेस को ज़्यादा पसंद के मुताबिक बनाने की ज़रूरत पड़ सकती है. ScreenRecoveryUI का सबक्लास बनाने के बजाय, सीधे उसकी पैरंट क्लास RecoveryUI का सबक्लास बनाएं.

RecoveryUI में, यूज़र इंटरफ़ेस (यूआई) के निचले लेवल के ऑपरेशन मैनेज करने के तरीके हैं. जैसे, "डिसप्ले को टॉगल करना," "प्रोग्रेस बार अपडेट करना," "मेन्यू दिखाना," "मेन्यू में चुने गए विकल्प बदलना" वगैरह. अपने डिवाइस के लिए सही इंटरफ़ेस देने के लिए, इन तरीकों को बदला जा सकता है. हो सकता है कि आपके डिवाइस में एलईडी हों, जहां स्थिति दिखाने के लिए अलग-अलग रंगों या फ़्लैश करने के पैटर्न का इस्तेमाल किया जा सकता है. इसके अलावा, हो सकता है कि आपके पास ऑडियो चलाने का विकल्प भी हो. (शायद आपको मेन्यू या "टेक्स्ट डिसप्ले" मोड का इस्तेमाल नहीं करना है; CheckKey() और HandleMenuKey() को लागू करके, इनका ऐक्सेस रोका जा सकता है. ऐसा करने पर, डिसप्ले कभी भी टॉगल नहीं होगा या कोई मेन्यू आइटम नहीं चुना जाएगा. इस मामले में, आपको RecoveryUI के कई तरीकों को सिर्फ़ खाली स्टब के तौर पर देना पड़ सकता है.

RecoveryUI के एलान के लिए bootable/recovery/ui.h देखें, ताकि यह पता चल सके कि आपको किन तरीकों का इस्तेमाल करना होगा. RecoveryUI एक एब्स्ट्रैक्ट क्लास है—कुछ तरीके पूरी तरह से वर्चुअल होते हैं और इन्हें सबक्लास से उपलब्ध कराना ज़रूरी होता है—हालांकि, इसमें मुख्य इनपुट को प्रोसेस करने का कोड होता है. अगर आपके डिवाइस में कुंजियां नहीं हैं या आपको उन्हें अलग तरीके से प्रोसेस करना है, तो इस सेटिंग को भी बदला जा सकता है.

अपडेटर

अपडेट पैकेज को इंस्टॉल करते समय, डिवाइस के हिसाब से कोड का इस्तेमाल किया जा सकता है. इसके लिए, आपको अपने ऐसे एक्सटेंशन फ़ंक्शन देने होंगे जिन्हें अपडेटर स्क्रिप्ट से कॉल किया जा सकता है. यहां tardis डिवाइस के लिए, फ़ंक्शन का एक सैंपल दिया गया है:

device/yoyodyne/tardis/recovery/recovery_updater.c
#include <stdlib.h>
#include <string.h>

#include "edify/expr.h"

हर एक्सटेंशन फ़ंक्शन का एक ही सिग्नेचर होता है. आर्ग्युमेंट में, फ़ंक्शन का नाम, State* कुकी, इनकमिंग आर्ग्युमेंट की संख्या, और आर्ग्युमेंट दिखाने वाले Expr* पॉइंटर का ऐरे होता है. रिटर्न वैल्यू, एक नई Value* होती है.

Value* ReprogramTardisFn(const char* name, State* state, int argc, Expr* argv[]) {
    if (argc != 2) {
        return ErrorAbort(state, "%s() expects 2 args, got %d", name, argc);
    }

फ़ंक्शन को कॉल करने के समय, आपके आर्ग्युमेंट का आकलन नहीं किया गया है—आपके फ़ंक्शन के लॉजिक से यह तय होता है कि किन आर्ग्युमेंट का आकलन किया जाए और कितनी बार किया जाए. इसलिए, अपने कंट्रोल स्ट्रक्चर लागू करने के लिए, एक्सटेंशन फ़ंक्शन का इस्तेमाल किया जा सकता है. Call Evaluate() का इस्तेमाल करके, किसी Expr* आर्ग्युमेंट का आकलन करें. इससे Value* वैल्यू मिलती है. अगर Evaluate() NULL दिखाता है, तो आपको अपने सभी संसाधनों को खाली कर देना चाहिए और तुरंत NULL दिखाना चाहिए (इससे edify स्टैक में, प्रोसेस को बीच में रोकने की जानकारी भेजी जाती है). ऐसा न होने पर, आपको रिटर्न की गई वैल्यू का मालिकाना हक मिल जाता है और आखिर में उस पर FreeValue() को कॉल करने की ज़िम्मेदारी आपकी होती है.

मान लें कि फ़ंक्शन के लिए दो आर्ग्युमेंट की ज़रूरत है: स्ट्रिंग वैल्यू वाली key और ब्लॉब वैल्यू वाली image. आर्ग्युमेंट को इस तरह पढ़ा जा सकता है:

   Value* key = EvaluateValue(state, argv[0]);
    if (key == NULL) {
        return NULL;
    }
    if (key->type != VAL_STRING) {
        ErrorAbort(state, "first arg to %s() must be string", name);
        FreeValue(key);
        return NULL;
    }
    Value* image = EvaluateValue(state, argv[1]);
    if (image == NULL) {
        FreeValue(key);    // must always free Value objects
        return NULL;
    }
    if (image->type != VAL_BLOB) {
        ErrorAbort(state, "second arg to %s() must be blob", name);
        FreeValue(key);
        FreeValue(image)
        return NULL;
    }

एक से ज़्यादा आर्ग्युमेंट के लिए, NULL की जांच करना और पहले से आकलन किए गए आर्ग्युमेंट को खाली करना मुश्किल हो सकता है. ReadValueArgs() फ़ंक्शन की मदद से, यह आसानी से किया जा सकता है. ऊपर दिए गए कोड के बजाय, यह लिखा जा सकता था:

   Value* key;
    Value* image;
    if (ReadValueArgs(state, argv, 2, &key, &image) != 0) {
        return NULL;     // ReadValueArgs() will have set the error message
    }
    if (key->type != VAL_STRING || image->type != VAL_BLOB) {
        ErrorAbort(state, "arguments to %s() have wrong type", name);
        FreeValue(key);
        FreeValue(image)
        return NULL;
    }

ReadValueArgs(), टाइप की जांच नहीं करता. इसलिए, आपको यहां यह काम करना होगा. एक if स्टेटमेंट के साथ ऐसा करना ज़्यादा आसान होता है. हालांकि, गड़बड़ी होने पर, आपको ज़्यादा सटीक गड़बड़ी का मैसेज नहीं मिलता. हालांकि, ReadValueArgs() हर आर्ग्युमेंट का आकलन करता है और अगर किसी भी आर्ग्युमेंट का आकलन नहीं हो पाता है, तो पहले से आकलन किए गए सभी आर्ग्युमेंट को खाली कर देता है. साथ ही, गड़बड़ी का एक काम का मैसेज भी सेट करता है. अलग-अलग संख्या वाले आर्ग्युमेंट का आकलन करने के लिए, ReadValueVarArgs() सुविधा फ़ंक्शन का इस्तेमाल किया जा सकता है. यह Value* का ऐरे दिखाता है.

आर्ग्युमेंट का आकलन करने के बाद, फ़ंक्शन का काम करें:

   // key->data is a NUL-terminated string
    // image->data and image->size define a block of binary data
    //
    // ... some device-specific magic here to
    // reprogram the tardis using those two values ...

रिटर्न वैल्यू, Value* ऑब्जेक्ट होनी चाहिए. इस ऑब्जेक्ट का मालिकाना हक, कॉल करने वाले को मिलेगा. कॉलर, इस Value* से दिखाए गए किसी भी डेटा का मालिकाना हक लेता है—खास तौर पर, datamember का.

इस मामले में, आपको सही या गलत वैल्यू दिखानी है, ताकि यह पता चल सके कि फ़ंक्शन सही तरीके से काम कर रहा है या नहीं. याद रखें कि खाली स्ट्रिंग गलत होती है और बाकी सभी स्ट्रिंग सही होती हैं. आपको वैल्यू ऑब्जेक्ट को malloc करना होगा, जिसमें रिटर्न करने के लिए, कॉन्स्टेंट स्ट्रिंग की malloc की गई कॉपी होनी चाहिए. ऐसा इसलिए, क्योंकि कॉलर दोनों को free() करेगा. आर्ग्युमेंट का आकलन करके मिले ऑब्जेक्ट पर FreeValue() को कॉल करना न भूलें!

   FreeValue(key);
    FreeValue(image);

    Value* result = malloc(sizeof(Value));
    result->type = VAL_STRING;
    result->data = strdup(successful ? "t" : "");
    result->size = strlen(result->data);
    return result;
}

सुविधाजनक फ़ंक्शन StringValue(), किसी स्ट्रिंग को नए वैल्यू ऑब्जेक्ट में रैप करता है. ऊपर दिए गए कोड को कम शब्दों में लिखने के लिए, इसका इस्तेमाल करें:

   FreeValue(key);
    FreeValue(image);

    return StringValue(strdup(successful ? "t" : ""));
}

edify इंटरप्रिटर में फ़ंक्शन जोड़ने के लिए, फ़ंक्शन Register_foo दें. यहां foo, उस स्टैटिक लाइब्रेरी का नाम है जिसमें यह कोड मौजूद है. हर एक्सटेंशन फ़ंक्शन को रजिस्टर करने के लिए, RegisterFunction() को कॉल करें. डिवाइस के हिसाब से फ़ंक्शन के नाम device.whatever रखें, ताकि आने वाले समय में जोड़े जाने वाले बिल्ट-इन फ़ंक्शन से कोई टकराव न हो.

void Register_librecovery_updater_tardis() {
    RegisterFunction("tardis.reprogram", ReprogramTardisFn);
}

अब अपने कोड की मदद से स्टैटिक लाइब्रेरी बनाने के लिए, मेकफ़ाइल को कॉन्फ़िगर किया जा सकता है. (यह वही makefile है जिसका इस्तेमाल, पिछले सेक्शन में रिकवरी यूज़र इंटरफ़ेस (यूआई) को पसंद के मुताबिक बनाने के लिए किया गया था. हो सकता है कि आपके डिवाइस में, यहां बताई गई दोनों स्टैटिक लाइब्रेरी मौजूद हों.)

device/yoyodyne/tardis/recovery/Android.mk
include $(CLEAR_VARS)
LOCAL_SRC_FILES := recovery_updater.c
LOCAL_C_INCLUDES += bootable/recovery

स्टैटिक लाइब्रेरी का नाम, उसमें मौजूद Register_libname फ़ंक्शन के नाम से मेल खाना चाहिए.

LOCAL_MODULE := librecovery_updater_tardis
include $(BUILD_STATIC_LIBRARY)

आखिर में, अपनी लाइब्रेरी को वापस लाने के लिए, रिकवरी का बिल्ड कॉन्फ़िगर करें. अपनी लाइब्रेरी को TARGET_RECOVERY_UPDATER_LIBS में जोड़ें. इसमें कई लाइब्रेरी हो सकती हैं और वे सभी रजिस्टर हो जाती हैं. अगर आपका कोड ऐसी अन्य स्टैटिक लाइब्रेरी पर निर्भर करता है जो खुद edify एक्सटेंशन नहीं हैं (जैसे, Register_libname फ़ंक्शन नहीं है), तो उन्हें टारगेट रिकवरी अपडेटर के अतिरिक्त लाइब्रेरी में शामिल किया जा सकता है. इससे, उन्हें अपडेटर से लिंक किया जा सकता है. इसके लिए, उनके रजिस्ट्रेशन फ़ंक्शन को कॉल करने की ज़रूरत नहीं होती. उदाहरण के लिए, अगर आपके डिवाइस के हिसाब से बने कोड को डेटा को डिकंप्रेस करने के लिए zlib का इस्तेमाल करना है, तो आपको यहां libz शामिल करना होगा.

device/yoyodyne/tardis/BoardConfig.mk
 [...]

# add device-specific extensions to the updater binary
TARGET_RECOVERY_UPDATER_LIBS += librecovery_updater_tardis
TARGET_RECOVERY_UPDATER_EXTRA_LIBS +=

आपके ओटीए पैकेज में मौजूद अपडेटर स्क्रिप्ट, अब आपके फ़ंक्शन को किसी भी दूसरे फ़ंक्शन की तरह कॉल कर सकती हैं. अपने TARDIS डिवाइस को फिर से प्रोग्राम करने के लिए, अपडेट स्क्रिप्ट में ये शामिल हो सकते हैं: tardis.reprogram("the-key", package_extract_file("tardis-image.dat")) . यह बिल्ट-इन फ़ंक्शन package_extract_file() के एक आर्ग्युमेंट वाले वर्शन का इस्तेमाल करता है. यह फ़ंक्शन, अपडेट पैकेज से निकाली गई फ़ाइल के कॉन्टेंट को ब्लॉब के तौर पर दिखाता है, ताकि नए एक्सटेंशन फ़ंक्शन के लिए दूसरा आर्ग्युमेंट बनाया जा सके.

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

आखिरी कॉम्पोनेंट, ओटीए पैकेज जनरेशन टूल की मदद से, आपके डिवाइस के हिसाब से डेटा के बारे में जानकारी पाता है और अपडेटर स्क्रिप्ट को एमिट करता है. इन स्क्रिप्ट में, आपके एक्सटेंशन फ़ंक्शन के कॉल शामिल होते हैं.

सबसे पहले, डिवाइस के हिसाब से डेटा के ब्लॉब के बारे में जानने के लिए, बिल्ड सिस्टम को सूचना दें. मान लें कि आपकी डेटा फ़ाइल device/yoyodyne/tardis/tardis.dat में है, तो अपने डिवाइस के AndroidBoard.mk में यह एलान करें:

device/yoyodyne/tardis/AndroidBoard.mk
  [...]

$(call add-radio-file,tardis.dat)

इसके बजाय, इसे Android.mk में भी डाला जा सकता है. हालांकि, इसके लिए डिवाइस की जांच की ज़रूरत होती है, क्योंकि ट्री में मौजूद सभी Android.mk फ़ाइलें लोड हो जाती हैं. भले ही, किसी भी डिवाइस को बनाया जा रहा हो. (अगर आपके ट्री में एक से ज़्यादा डिवाइस शामिल हैं, तो सिर्फ़ tardis डिवाइस बनाते समय tardis.dat फ़ाइल जोड़ें.)

device/yoyodyne/tardis/Android.mk
  [...]

# an alternative to specifying it in AndroidBoard.mk
ifeq (($TARGET_DEVICE),tardis)
  $(call add-radio-file,tardis.dat)
endif

इन्हें रेडियो फ़ाइलें इसलिए कहा जाता है, क्योंकि ये पुरानी हैं. ऐसा हो सकता है कि इनका डिवाइस के रेडियो (अगर मौजूद है) से कोई लेना-देना न हो. ये डेटा के ऐसे ब्लॉब होते हैं जिन्हें बिल्ड सिस्टम, टारगेट-फ़ाइलों .zip में कॉपी करता है. इनका इस्तेमाल, ओटीए जनरेशन टूल करते हैं. बिल्ड करने पर, tardis.dat को RADIO/tardis.dat के तौर पर target-files.zip में सेव किया जाता है. जितनी चाहें उतनी फ़ाइलें जोड़ने के लिए, add-radio-file को कई बार कॉल किया जा सकता है.

Python मॉड्यूल

रिलीज़ टूल का इस्तेमाल करने के लिए, Python मॉड्यूल लिखें. इसका नाम releasetools.py होना चाहिए. अगर यह मॉड्यूल मौजूद है, तो टूल इसे कॉल कर सकते हैं. उदाहरण:

device/yoyodyne/tardis/releasetools.py
import common

def FullOTA_InstallEnd(info):
  # copy the data into the package.
  tardis_dat = info.input_zip.read("RADIO/tardis.dat")
  common.ZipWriteStr(info.output_zip, "tardis.dat", tardis_dat)

  # emit the script code to install this data on the device
  info.script.AppendExtra(
      """tardis.reprogram("the-key", package_extract_file("tardis.dat"));""")

इंक्रीमेंटल ओटीए पैकेज जनरेट करने के मामले को एक अलग फ़ंक्शन मैनेज करता है. उदाहरण के लिए, मान लें कि आपको tardis को सिर्फ़ तब फिर से प्रोग्राम करना है, जब दो बिल्ड के बीच tardis.dat फ़ाइल में बदलाव हुआ हो.

def IncrementalOTA_InstallEnd(info):
  # copy the data into the package.
  source_tardis_dat = info.source_zip.read("RADIO/tardis.dat")
  target_tardis_dat = info.target_zip.read("RADIO/tardis.dat")

  if source_tardis_dat == target_tardis_dat:
      # tardis.dat is unchanged from previous build; no
      # need to reprogram it
      return

  # include the new tardis.dat in the OTA package
  common.ZipWriteStr(info.output_zip, "tardis.dat", target_tardis_dat)

  # emit the script code to install this data on the device
  info.script.AppendExtra(
      """tardis.reprogram("the-key", package_extract_file("tardis.dat"));""")

मॉड्यूल फ़ंक्शन

मॉड्यूल में ये फ़ंक्शन दिए जा सकते हैं. हालांकि, सिर्फ़ ज़रूरी फ़ंक्शन लागू करें.

FullOTA_Assertions()
पूरा ओटीए जनरेट करने की शुरुआत में कॉल किया जाता है. डिवाइस की मौजूदा स्थिति के बारे में जानकारी देने के लिए, यह एक अच्छी जगह है. ऐसे स्क्रिप्ट निर्देश न दें जिनसे डिवाइस में बदलाव हो.
FullOTA_InstallBegin()
डिवाइस की स्थिति के बारे में सभी दावे पास होने के बाद, लेकिन कोई बदलाव किए जाने से पहले कॉल किया जाता है. डिवाइस के हिसाब से अपडेट के लिए निर्देश दिए जा सकते हैं. ये निर्देश, डिवाइस पर किसी भी तरह का बदलाव किए जाने से पहले लागू होने चाहिए.
FullOTA_InstallEnd()
स्क्रिप्ट जनरेट होने के आखिर में, इसे कॉल किया जाता है. ऐसा तब किया जाता है, जब स्क्रिप्ट के निर्देशों के ज़रिए, बूट और सिस्टम के सेगमेंट अपडेट हो जाते हैं. डिवाइस के हिसाब से अपडेट पाने के लिए, आपके पास और भी निर्देश देने का विकल्प है.
IncrementalOTA_Assertions()
FullOTA_Assertions() जैसा ही, लेकिन इसे इंक्रीमेंटल अपडेट पैकेज जनरेट करते समय कॉल किया जाता है.
IncrementalOTA_VerifyBegin()
डिवाइस की स्थिति के बारे में सभी दावे पास होने के बाद, लेकिन कोई बदलाव किए जाने से पहले कॉल किया जाता है. डिवाइस के हिसाब से अपडेट के लिए निर्देश दिए जा सकते हैं. ये निर्देश, डिवाइस पर किसी भी और चीज़ में बदलाव होने से पहले लागू होने चाहिए.
IncrementalOTA_VerifyEnd()
पुष्टि के चरण के आखिर में, स्क्रिप्ट को कॉल किया जाता है. ऐसा तब होता है, जब स्क्रिप्ट यह पुष्टि कर लेती है कि जिन फ़ाइलों में बदलाव किया जाएगा उनमें शुरुआती कॉन्टेंट सही है. इस समय, डिवाइस पर कुछ भी नहीं बदला गया है. डिवाइस के हिसाब से, पुष्टि करने के लिए ज़्यादा कोड भी उत्सर्जित किए जा सकते हैं.
IncrementalOTA_InstallBegin()
पैच की जाने वाली फ़ाइलों की पुष्टि करने के बाद, जब उनमें कोई बदलाव न किया गया हो, तब इसे कॉल किया जाता है. पुष्टि करने के दौरान, यह पता लगाया जाता है कि फ़ाइलों की स्थिति पहले जैसी है या नहीं. डिवाइस के हिसाब से उन अपडेट के लिए निर्देश दिए जा सकते हैं जिन्हें डिवाइस पर किसी भी चीज़ में बदलाव करने से पहले चलाना ज़रूरी है.
IncrementalOTA_InstallEnd()
अपने पूरे ओटीए पैकेज के जैसे ही, इसे स्क्रिप्ट जनरेट करने के आखिर में, बूट और सिस्टम के सेगमेंट को अपडेट करने के लिए स्क्रिप्ट के निर्देशों के बाद, कहा जाता है. डिवाइस के हिसाब से अपडेट के लिए, अन्य निर्देश भी दिए जा सकते हैं.

ध्यान दें: अगर डिवाइस की बैटरी खत्म हो जाती है, तो हो सकता है कि ओटीए इंस्टॉलेशन फिर से शुरू हो जाए. उन डिवाइसों के लिए तैयार रहें जिन पर ये निर्देश पहले से ही पूरी तरह या कुछ हद तक लागू हो चुके हैं.

जानकारी वाले ऑब्जेक्ट में फ़ंक्शन पास करना

फ़ंक्शन को एक ऐसे जानकारी वाले ऑब्जेक्ट में पास करें जिसमें कई काम के आइटम शामिल हों:

  • info.input_zip. (सिर्फ़ पूरे ओटीए के लिए) इनपुट टारगेट-फ़ाइलों .zip के लिए zipfile.ZipFile ऑब्जेक्ट.
  • info.source_zip. (सिर्फ़ इंक्रीमेंटल ओटीए के लिए) सोर्स टारगेट-फ़ाइलों .zip के लिए zipfile.ZipFile ऑब्जेक्ट (इंक्रीमेंटल पैकेज इंस्टॉल होने पर, डिवाइस पर पहले से मौजूद बिल्ड).
  • info.target_zip. (सिर्फ़ इंक्रीमेंटल ओटीए के लिए) टारगेट target-files .zip के लिए zipfile.ZipFile ऑब्जेक्ट (इंक्रीमेंटल पैकेज, डिवाइस पर जो बिल्ड डालता है).
  • info.output_zip. पैकेज बनाया जा रहा है; लिखने के लिए खोला गया zipfile.ZipFile ऑब्जेक्ट. पैकेज में कोई फ़ाइल जोड़ने के लिए, common.ZipWriteStr(info.output_zip, filename, data) का इस्तेमाल करें.
  • info.script. स्क्रिप्ट ऑब्जेक्ट, जिसमें निर्देश जोड़े जा सकते हैं. स्क्रिप्ट में टेक्स्ट दिखाने के लिए, info.script.AppendExtra(script_text) को कॉल करें. पक्का करें कि आउटपुट टेक्स्ट के आखिर में सेमीकोलन हो, ताकि वह बाद में उत्सर्जित किए गए निर्देशों में न चला जाए.

जानकारी वाले ऑब्जेक्ट के बारे में ज़्यादा जानने के लिए, ZIP संग्रह के लिए Python Software Foundation का दस्तावेज़ देखें.

मॉड्यूल की जगह की जानकारी देना

BoardConfig.mk फ़ाइल में, अपने डिवाइस की releasetools.py स्क्रिप्ट की जगह की जानकारी दें:

device/yoyodyne/tardis/BoardConfig.mk
 [...]

TARGET_RELEASETOOLS_EXTENSIONS := device/yoyodyne/tardis

अगर TARGET_RELEASETOOLS_EXTENSIONS सेट नहीं है, तो यह डिफ़ॉल्ट रूप से $(TARGET_DEVICE_DIR)/../common डायरेक्ट्री (इस उदाहरण में device/yoyodyne/common ) पर सेट होता है. releasetools.py स्क्रिप्ट की जगह की जानकारी साफ़ तौर पर देना सबसे अच्छा होता है. tardis डिवाइस बनाते समय, releasetools.py स्क्रिप्ट को टारगेट-फ़ाइलों वाली .zip फ़ाइल (META/releasetools.py ) में शामिल किया जाता है.

रिलीज़ टूल (img_from_target_files या ota_from_target_files) चलाने पर, अगर target-files.zip में releasetools .py स्क्रिप्ट मौजूद है, तो Android सोर्स ट्री में मौजूद स्क्रिप्ट के बजाय, उस स्क्रिप्ट को प्राथमिकता दी जाती है. -s (या --device_specific) विकल्प की मदद से, डिवाइस के हिसाब से एक्सटेंशन के पाथ की जानकारी भी साफ़ तौर पर दी जा सकती है. यह विकल्प सबसे ज़्यादा प्राथमिकता पाता है. इसकी मदद से, गड़बड़ियों को ठीक किया जा सकता है और releasetools एक्सटेंशन में बदलाव किए जा सकते हैं. साथ ही, उन बदलावों को पुरानी टारगेट-फ़ाइलों पर लागू किया जा सकता है.

अब ota_from_target_files को चलाने पर, यह target_files .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

इसके अलावा, ota_from_target_files को चलाते समय, डिवाइस के हिसाब से एक्सटेंशन तय किए जा सकते हैं.

./build/make/tools/releasetools/ota_from_target_files \
    -s device/yoyodyne/tardis \
    -i PREVIOUS-tardis-target_files.zip \
    dist_output/tardis-target_files.zip \
    incremental_ota_update.zip

ध्यान दें: विकल्पों की पूरी सूची के लिए, build/make/tools/releasetools/ota_from_target_files में ota_from_target_files टिप्पणियां देखें.

साइड-लोड करने का तरीका

रिकवरी में साइडलोडिंग की सुविधा होती है. इसकी मदद से, अपडेट पैकेज को मैन्युअल तरीके से इंस्टॉल किया जा सकता है. इसके लिए, मुख्य सिस्टम से इसे ओवर-द-एयर डाउनलोड करने की ज़रूरत नहीं होती. साइडलोड करने की सुविधा, डिबग करने या उन डिवाइसों में बदलाव करने के लिए काम की है जिनमें मुख्य सिस्टम को बूट नहीं किया जा सकता.

आम तौर पर, डिवाइस के एसडी कार्ड से पैकेज लोड करके साइडलोडिंग की जाती है. अगर डिवाइस बूट नहीं हो रहा है, तो किसी दूसरे कंप्यूटर का इस्तेमाल करके पैकेज को एसडी कार्ड में डाला जा सकता है. इसके बाद, एसडी कार्ड को डिवाइस में डाला जा सकता है. रिपेयर टूल, ऐसे Android डिवाइसों के लिए साइडलोड करने के दो अतिरिक्त तरीकों का इस्तेमाल करता है जिनमें हटाया जा सकने वाला बाहरी स्टोरेज नहीं होता: कैश मेमोरी से पैकेज लोड करना और adb का इस्तेमाल करके, यूएसबी से पैकेज लोड करना.

साइडलोड करने के हर तरीके को चालू करने के लिए, आपके डिवाइस का Device::InvokeMenuItem() तरीका इनमें से कोई एक वैल्यू दिखा सकता है:

  • APPLY_EXT. बाहरी स्टोरेज ( /sdcard डायरेक्ट्री) से अपडेट पैकेज को अलग से लोड करें. आपके recovery.fstab में /sdcard माउंट पॉइंट की जानकारी होनी चाहिए. इसे उन डिवाइसों पर इस्तेमाल नहीं किया जा सकता जो /data (या मिलते-जुलते किसी तरीके) के लिए लिंक किए गए एसडी कार्ड को एमुलेट करते हैं. आम तौर पर, /data को वापस पाने का विकल्प उपलब्ध नहीं होता, क्योंकि इसे एन्क्रिप्ट (सुरक्षित) किया जा सकता है. रिकवरी यूज़र इंटरफ़ेस (यूआई), /sdcard में .zip फ़ाइलों का मेन्यू दिखाता है और उपयोगकर्ता को इनमें से किसी एक को चुनने की अनुमति देता है.
  • APPLY_CACHE. /sdcard से पैकेज लोड करने की तरह ही, इसके लिए भी /cache डायरेक्ट्री का इस्तेमाल किया जाता है. यह डायरेक्ट्री, रिकवरी के लिए हमेशा उपलब्ध होती है. सामान्य सिस्टम में, /cache में सिर्फ़ खास सुविधाओं वाले उपयोगकर्ता ही लिख सकते हैं. अगर डिवाइस बूट नहीं हो पा रहा है, तो /cache डायरेक्ट्री में बिल्कुल भी लिखा नहीं जा सकता. इस वजह से, इस मैकेनिज्म का इस्तेमाल सीमित तौर पर किया जा सकता है.
  • APPLY_ADB_SIDELOAD. इससे उपयोगकर्ता को यूएसबी केबल और adb डेवलपमेंट टूल की मदद से, डिवाइस पर पैकेज भेजने की अनुमति मिलती है. इस तरीके का इस्तेमाल करने पर, रिकवरी मोड अपने adbd डेमन का छोटा वर्शन शुरू करता है, ताकि कनेक्ट किए गए होस्ट कंप्यूटर पर मौजूद adb, उससे बात कर सके. इस छोटे वर्शन में सिर्फ़ एक निर्देश काम करता है: adb sideload filename. नाम वाली फ़ाइल को होस्ट मशीन से डिवाइस पर भेजा जाता है. इसके बाद, डिवाइस उस फ़ाइल की पुष्टि करता है और उसे ठीक वैसे ही इंस्टॉल करता है जैसे वह लोकल स्टोरेज में मौजूद हो.

ध्यान रखने वाली कुछ बातें:

  • सिर्फ़ यूएसबी ट्रांसपोर्ट का इस्तेमाल किया जा सकता है.
  • अगर आपकी रिकवरी में adbd सामान्य रूप से काम करता है (आम तौर पर, userdebug और eng बिल्ड के लिए ऐसा होता है), तो डिवाइस के adb साइडलोड मोड में होने पर, वह बंद हो जाएगा. साथ ही, adb साइडलोड से पैकेज मिलने के बाद, उसे फिर से चालू कर दिया जाएगा. adb साइडलोड मोड में, sideload के अलावा कोई भी adb निर्देश काम नहीं करता. logcat, reboot, push, pull , shell वगैरह सभी काम नहीं करते.
  • डिवाइस पर adb साइडलोड मोड से बाहर नहीं निकला जा सकता. प्रोसेस को रोकने के लिए, पैकेज के तौर पर /dev/null (या कोई ऐसा अन्य पैकेज जो मान्य न हो) भेजा जा सकता है. ऐसा करने पर, डिवाइस उसकी पुष्टि नहीं कर पाएगा और इंस्टॉलेशन की प्रोसेस रुक जाएगी. RecoveryUI के लागू होने के CheckKey() तरीके को बटन दबाने पर भी कॉल किया जाएगा, ताकि आप एक ऐसा बटन क्रम दे सकें जो डिवाइस को रीबूट करे और adb साइडलोड मोड में काम करे.