डिवाइस के हिसाब से कोड

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

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

पार्टिशन मैप

Android 2.3 के बाद के वर्शन में, eMMc फ़्लैश डिवाइसों और उन डिवाइसों पर चलने वाले ext4 फ़ाइलसिस्टम के साथ काम करता है. यह मेमोरी टेक्नोलॉजी डिवाइस (एमटीडी) फ़्लैश डिवाइसों और पुरानी रिलीज़ के 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 को असल में माउंट नहीं किया जाता है, लेकिन माउंट पॉइंट का इस्तेमाल पार्टीशन का पता लगाने के लिए किया जाता है. "device" /proc/mtd में मौजूद एमटीडी पार्टीशन का नाम होना चाहिए.
ext4
eMMc फ़्लैश डिवाइस के ऊपर ext4 फ़ाइलसिस्टम. "device" ब्लॉक किए गए डिवाइस का पाथ होना चाहिए.
emmc
यह एक रॉ ईएमएमसी ब्लॉक डिवाइस है. इसका इस्तेमाल, बूट और रिकवरी जैसे बूट किए जा सकने वाले पार्टीशन के लिए किया जाता है. mtd टाइप की तरह ही, eMMc को कभी भी माउंट नहीं किया जाता. हालांकि, माउंट पॉइंट स्ट्रिंग का इस्तेमाल टेबल में डिवाइस का पता लगाने के लिए किया जाता है.
vfat
यह ब्लॉक डिवाइस के ऊपर मौजूद FAT फ़ाइल सिस्टम है. आम तौर पर, इसका इस्तेमाल एसडी कार्ड जैसे बाहरी स्टोरेज के लिए किया जाता है. डिवाइस, ब्लॉक डिवाइस है. device2, दूसरा ब्लॉक डिवाइस है. अगर प्राइमरी डिवाइस को माउंट नहीं किया जा सकता, तो सिस्टम इसे माउंट करने की कोशिश करता है. ऐसा इसलिए किया जाता है, ताकि एसडी कार्ड के साथ काम किया जा सके. एसडी कार्ड को पार्टीशन टेबल के साथ फ़ॉर्मैट किया जा सकता है या नहीं भी किया जा सकता.

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

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

ध्यान दें: 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 };

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

Customize 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 कॉन्स्टेंट

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

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

CheckKey() को तब कॉल किया जाता है, जब एक ही कुंजी के लिए key-down इवेंट के बाद key-up इवेंट होता है. (A-down B-down B-up A-up इवेंट के क्रम से, सिर्फ़ 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 एफ़पीएस) डिफ़ॉल्ट रिकवरी इमेज से मेल खाती है. इन इमेज का इस्तेमाल करते समय, आपको 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 कोड वाले इनपुट इवेंट जनरेट करता है), तो रिकवरी, KEY_UP और KEY_DOWN कीप्रेस को सिंथेसाइज़ करती है. ऐसा तब होता है, जब ट्रैकबॉल जैसा इनपुट डिवाइस, Y ऐक्सिस में मोशन की रिपोर्ट करता है. आपको सिर्फ़ 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 की वैल्यू false है, तो मेन्यू में बदलाव करने वाली खास वैल्यू (हाइलाइट को मूव करना, हाइलाइट किए गए आइटम को चालू करना) को वापस लाने का कोई मतलब नहीं है. ऐसा इसलिए, क्योंकि उपयोगकर्ता को हाइलाइट नहीं दिखती. हालांकि, अगर आपको वैल्यू वापस लानी हैं, तो ऐसा किया जा सकता है.

InvokeMenuItem

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

   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 enum का कोई भी सदस्य वापस आ सकता है. इससे सिस्टम को उस कार्रवाई को करने का निर्देश मिलता है. अगर आपको सिस्टम से कोई कार्रवाई नहीं करानी है, तो NO_ACTION सदस्य का इस्तेमाल करें. यह ऐसी जगह है जहां सिस्टम में मौजूद सुविधा के अलावा, डेटा वापस पाने की अतिरिक्त सुविधा दी जा सकती है: इसके लिए, अपने मेन्यू में एक आइटम जोड़ें. जब उस मेन्यू आइटम को चालू किया जाए, तब उसे यहां लागू करें. साथ ही, NO_ACTION वैल्यू दिखाएं, ताकि सिस्टम कोई अन्य कार्रवाई न करे.

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

  • NO_ACTION. कुछ न करें.
  • REBOOT. रिकवरी मोड से बाहर निकलें और डिवाइस को सामान्य तरीके से रीबूट करें.
  • 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

OTA इंस्टॉल करने के दौरान दिखने वाली इमेज

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

इंस्टॉल करने के ऐनिमेशन को एक PNG इमेज के तौर पर दिखाया गया है. इसमें ऐनिमेशन के अलग-अलग फ़्रेम, लाइन के हिसाब से इंटरलेस्ड किए गए हैं. यही वजह है कि इमेज 2 में, ऐनिमेशन के फ़्रेम एक-दूसरे में घुसे हुए दिख रहे हैं. उदाहरण के लिए, 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 और इससे पहले के वर्शन में, रिकवरी यूज़र इंटरफ़ेस (यूआई) में error इमेज (ऊपर दिखाई गई है) और installing ऐनिमेशन के साथ-साथ कई ओवरले इमेज का इस्तेमाल किया जाता है:

OTA इंस्टॉल करने के दौरान दिखने वाली इमेज

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

इमेज को पहले ओवरले के तौर पर दिखाया गया है

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

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

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

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

install plus first overlay की कंपोज़िट इमेज

छठी इमेज. ऐनिमेशन फ़्रेम 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

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

दसवीं इमेज. progress_fill.png

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

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

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

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

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

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

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

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

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

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

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

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

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

अपडेटर

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

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() को कॉल करने की ज़िम्मेदारी भी आपकी होती है.

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

   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 का.

इस उदाहरण में, आपको यह दिखाने के लिए कि कार्रवाई पूरी हो गई है, सही या गलत वैल्यू दिखानी है. यह याद रखें कि खाली स्ट्रिंग false होती है और बाकी सभी स्ट्रिंग true होती हैं. आपको एक वैल्यू ऑब्जेक्ट को malloc करना होगा. साथ ही, लौटाने के लिए कॉन्स्टेंट स्ट्रिंग की 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(), किसी स्ट्रिंग को नए Value ऑब्जेक्ट में रैप करता है. ऊपर दिए गए कोड को ज़्यादा सटीक तरीके से लिखने के लिए, इसका इस्तेमाल करें:

   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);
}

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

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 फ़ंक्शन नहीं है), तो उन्हें TARGET_RECOVERY_UPDATER_EXTRA_LIBS में लिस्ट किया जा सकता है. इससे उन्हें अपडेटर से लिंक किया जा सकेगा. इसके लिए, उनके (मौजूद नहीं) रजिस्ट्रेशन फ़ंक्शन को कॉल करने की ज़रूरत नहीं होगी. उदाहरण के लिए, अगर आपके डिवाइस के लिए खास तौर पर तैयार किया गया कोड, डेटा को डीकंप्रेस करने के लिए 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.reprogram("the-key", package_extract_file("tardis-image.dat")) . इसमें बिल्ट-इन फ़ंक्शन package_extract_file() के सिंगल-आर्ग्युमेंट वर्शन का इस्तेमाल किया जाता है. यह अपडेट पैकेज से निकाली गई फ़ाइल के कॉन्टेंट को एक ब्लोब के तौर पर दिखाता है, ताकि नए एक्सटेंशन फ़ंक्शन के लिए दूसरा आर्ग्युमेंट तैयार किया जा सके.

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

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

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

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

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

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

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 में कॉपी करता है. इस .zip का इस्तेमाल, ओटीए जनरेशन टूल करते हैं. बिल्ड करने पर, tardis.dat को target-files.zip में RADIO/tardis.dat के तौर पर सेव किया जाता है. आपको जितनी फ़ाइलें जोड़नी हैं उतनी बार 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.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()
इस फ़ंक्शन को तब कॉल किया जाता है, जब पैच की जाने वाली फ़ाइलों की पुष्टि हो जाती है कि वे before स्टेट में हैं. हालांकि, इस दौरान कोई बदलाव नहीं किया जाता. डिवाइस के हिसाब से अपडेट करने के लिए निर्देश दिए जा सकते हैं. ये निर्देश, डिवाइस में कोई भी बदलाव करने से पहले लागू होने चाहिए.
IncrementalOTA_InstallEnd()
यह फ़ुल ओटीए पैकेज के जैसा ही होता है. इसे स्क्रिप्ट जनरेट होने के आखिर में कॉल किया जाता है. ऐसा तब किया जाता है, जब बूट और सिस्टम पार्टीशन को अपडेट करने के लिए स्क्रिप्ट कमांड जारी कर दी जाती हैं. डिवाइस के हिसाब से अपडेट के लिए, अन्य निर्देश भी दिए जा सकते हैं.

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

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

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

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

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

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

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 स्क्रिप्ट की जगह की जानकारी साफ़ तौर पर देना सबसे अच्छा होता है. टारडिस डिवाइस बनाते समय, releasetools.py स्क्रिप्ट को target-files .zip फ़ाइल (META/releasetools.py ) में शामिल किया जाता है.

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

अब 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

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

साइडलोडिंग का तरीका

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

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

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

  • 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 sideload मोड में होने पर, इसे बंद कर दिया जाएगा. साथ ही, adb sideload के पैकेज मिलने के बाद, इसे फिर से शुरू कर दिया जाएगा. adb साइडलोड मोड में, sideload को छोड़कर कोई भी adb कमांड काम नहीं करती है. जैसे, logcat, reboot, push , pull, shell वगैरह.
  • डिवाइस पर adb sideload मोड से बाहर नहीं निकला जा सकता. इस प्रोसेस को रोकने के लिए, पैकेज के तौर पर /dev/null (या कोई और ऐसा आइटम जो मान्य पैकेज न हो) भेजा जा सकता है. इसके बाद, डिवाइस इसकी पुष्टि नहीं कर पाएगा और इंस्टॉलेशन की प्रोसेस बंद हो जाएगी. कीबोर्ड के बटन दबाने पर, RecoveryUI लागू करने के CheckKey() तरीके को कॉल किया जाता रहेगा. इसलिए, आपके पास एक ऐसा बटन क्रम देने का विकल्प होता है जिससे डिवाइस रीबूट हो जाता है और adb sideload मोड में काम करता है.