ডিভাইস-নির্দিষ্ট কোড

রিকভারি সিস্টেমে ডিভাইস-নির্দিষ্ট কোড যোগ করার জন্য একাধিক হুক থাকে যাতে OTA আপডেট 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 ছাড়া, যা ঐচ্ছিক, এই উদাহরণের সব মাউন্ট পয়েন্ট অবশ্যই উল্লেখ করতে হবে (ডিভাইস অতিরিক্ত পার্টিশনও যোগ করতে পারে)। পাঁচ ধরনের কাজ করে এমন ফাইলসিস্টেমের ধরন আছে:

yaffs2
MTD ফ্ল্যাশ ডিভাইসের উপরে yaffs2 ফাইলসিস্টেম। "ডিভাইস" অবশ্যই MTD পার্টিশনের নাম হতে হবে এবং /proc/mtd-এ অবশ্যই দেখা যেতে হবে।
mtd
একটি কাঁচা MTD পার্টিশন, যা বুট ও রিকভারির মতো বুটেবল পার্টিশনের জন্য ব্যবহার করা হয়। MTD আসলে মাউন্ট করা হয় না, তবে মাউন্ট পয়েন্টটি পার্টিশন লোকেট করার জন্য 'কী' হিসেবে ব্যবহার করা হয়। "device" /proc/mtd-এ MTD পার্টিশনের নাম হতে হবে।
ext4
eMMc ফ্ল্যাশ ডিভাইসের উপরে ext4 ফাইলসিস্টেম। "device" অবশ্যই ব্লক ডিভাইসের পাথ হতে হবে।
emmc
একটি কাঁচা eMMc ব্লক ডিভাইস, যা বুট এবং রিকভারির মতো বুটেবল পার্টিশনের জন্য ব্যবহৃত হয়। mtd ধরনের মতোই, eMMC কখনওই আসলে মাউন্ট করা হয় না, তবে মাউন্ট পয়েন্ট স্ট্রিংটি টেবিলের মধ্যে ডিভাইসটি লোকেট করতে ব্যবহার করা হয়।
vfat
ব্লক ডিভাইসের উপরে FAT ফাইলসিস্টেম, সাধারণত এসডি কার্ডের মতো এক্সটার্নাল স্টোরেজের জন্য। device হল ব্লক ডিভাইস; device2 হল দ্বিতীয় ব্লক ডিভাইস, প্রাথমিক ডিভাইস মাউন্ট করা না গেলে সিস্টেম এটি মাউন্ট করার চেষ্টা করে (এটি SD কার্ডের সাথে মানানসই হতে পারে, তবে পার্টিশন টেবিল দিয়ে ফর্ম্যাট করা নাও হতে পারে)।

সব পার্টিশনকে রুট ডিরেক্টরিতে মাউন্ট করতে হবে (অর্থাৎ, মাউন্ট পয়েন্ট ভ্যালু স্ল্যাশ দিয়ে শুরু হতে হবে এবং অন্য কোনও স্ল্যাশ থাকতে পারবে না)। এই বিধিনিষেধ শুধুমাত্র রিকভারিতে ফাইলসিস্টেম মাউন্ট করার ক্ষেত্রে প্রযোজ্য; মূল সিস্টেম যেকোনও জায়গায় সেগুলি মাউন্ট করতে পারে। ডিরেক্টরি /boot, /recovery ও /misc-কে অবশ্যই র ডেটা টাইপ (mtd বা emmc) হতে হবে, অন্যদিকে ডিরেক্টরি /system, /data, /cache ও /sdcard (উপলভ্য থাকলে) অবশ্যই ফাইলসিস্টেম ডেটা টাইপ (yaffs2, ext4 বা vfat) হতে হবে।

Android 3.0 থেকে শুরু করে, recovery.fstab ফাইলে একটি অতিরিক্ত ঐচ্ছিক ফিল্ড যোগ করা হয়েছে, options. বর্তমানে একমাত্র সংজ্ঞায়িত বিকল্প হল length , যা আপনাকে স্পষ্টভাবে পার্টিশনের দৈর্ঘ্য নির্দিষ্ট করতে দেয়। পার্টিশন রিফর্ম্যাট করার সময় এই দৈর্ঘ্য ব্যবহার করা হয় (যেমন, ডেটা মোছা/ফ্যাক্টরি রিসেট করার সময় userdata পার্টিশনের জন্য অথবা সম্পূর্ণ OTA প্যাকেজ ইনস্টল করার সময় system পার্টিশনের জন্য)। দৈর্ঘ্যের মান নেগেটিভ হলে, ফর্ম্যাট করার সাইজটি আসল পার্টিশন সাইজের সাথে দৈর্ঘ্যের মান যোগ করে পাওয়া যায়। যেমন, "length=-16384" সেট করার অর্থ হল, সেই পার্টিশন রিফর্ম্যাট করা হলে, সেটির শেষ ১৬কেবি ওভাররাইট করা হবে না। এটি ব্যবহারকারীর ডেটা পার্টিশনের এনক্রিপশনের মতো ফিচারকে সাপোর্ট করে (যেখানে এনক্রিপশন মেটাডেটা পার্টিশনের শেষে স্টোর করা হয় যা ওভাররাইট করা উচিত নয়)।

মনে রাখবেন: device2 ও options ফিল্ড ঐচ্ছিক, যা পার্সিংয়ে অস্পষ্টতা তৈরি করে। লাইনের চতুর্থ ফিল্ডে এন্ট্রিটি '/' ক্যারেক্টার দিয়ে শুরু হলে, এটিকে device2 এন্ট্রি হিসেবে বিবেচনা করা হয়; এন্ট্রিটি '/' ক্যারেক্টার দিয়ে শুরু না হলে, এটিকে বিকল্প ফিল্ড হিসেবে বিবেচনা করা হয়।

চালু অ্যানিমেশন

Android ডিভাইস বুট করার সময় যে অ্যানিমেশন দেখানো হয় তা কাস্টমাইজ করার ক্ষমতা ডিভাইস প্রস্তুতকারকদের আছে। এটি করতে, bootanimation ফর্ম্যাট নির্দিষ্টকরণ অনুযায়ী সাজানো ও লোকেটেড একটি .zip ফাইল তৈরি করুন।

Android Things ডিভাইসের জন্য, আপনি Android Things কনসোলে জিপ করা ফাইল আপলোড করতে পারেন যাতে বেছে নেওয়া প্রোডাক্টে ছবি অন্তর্ভুক্ত করা যায়।

মনে রাখবেন: এইসব ছবি Android ব্র্যান্ড নির্দেশিকা মেনে চলতে হবে। ব্র্যান্ড নির্দেশিকা পেতে, পার্টনার মার্কেটিং হাব-এর Android বিভাগ দেখুন।

রিকভারি UI

বিভিন্ন উপলভ্য হার্ডওয়্যার (ফিজিক্যাল বোতাম, LED, স্ক্রিন ইত্যাদি) সহ ডিভাইসকে সহায়তা করতে, আপনি রিকভারি ইন্টারফেস কাস্টমাইজ করে স্ট্যাটাস দেখাতে এবং প্রতিটি ডিভাইসের জন্য ম্যানুয়ালি পরিচালিত লুকানো ফিচার অ্যাক্সেস করতে পারবেন।

আপনার লক্ষ্য হল, ডিভাইস-নির্দিষ্ট কার্যকারিতা প্রদান করার জন্য কয়েকটি 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 কনস্ট্যান্ট

KEY_* ধ্রুবকগুলি linux/input.h-এ সংজ্ঞায়িত করা হয়েছে। CheckKey() is কল করা হয়, রিকভারির বাকি অংশে যাই ঘটুক না কেন: মেনু টগল করে বন্ধ করা হলে, যখন এটি চালু থাকে, প্যাকেজ ইনস্টলেশনের সময়, ব্যবহারকারীর ডেটা মোছার সময় ইত্যাদি। এটি চারটি কনস্ট্যান্টের মধ্যে একটি রিটার্ন করতে পারে:

  • টগল করুন। মেনু এবং/অথবা টেক্সট লগ টগল করে চালু বা বন্ধ করুন
  • রি-বুট করুন। অবিলম্বে ডিভাইস রিবুট করা
  • উপেক্ষা করুন। এই কীপ্রেস উপেক্ষা করুন
  • ENQUEUE. এই কীপ্রেসকে সিঙ্ক্রোনাসভাবে ব্যবহার করার জন্য এনকিউ করুন (অর্থাৎ, ডিসপ্লে চালু থাকলে রিকভারি মেনু সিস্টেমের মাধ্যমে)

একই 'কী'-এর জন্য 'কী'-ডাউন ইভেন্টের পরে 'কী'-আপ ইভেন্ট হলে, প্রতিবার CheckKey() কল করা হয়। (A-down B-down B-up A-up ইভেন্টের ক্রম শুধুমাত্র CheckKey(B) কল করার ফলাফল দেয়।) অন্যান্য কী প্রেস করে রাখা হয়েছে কিনা তা জানতে IsKeyPressed()-কে CheckKey() কল করতে পারে। (উপরের কী ইভেন্টের সিকোয়েন্সে, CheckKey(B) IsKeyPressed(A)-কে কল করলে সেটি true রিটার্ন করত।)

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-এর সাথে আপনার নিজের ছবি (সমস্যা আইকন, ইনস্টলেশন অ্যানিমেশন, প্রগ্রেস বার) ব্যবহার করার সময়, আপনি অ্যানিমেশনের ফ্রেম প্রতি সেকেন্ড (FPS) স্পিড কন্ট্রোল করতে animation_fps ভেরিয়েবল সেট করতে পারবেন।

মনে রাখবেন: বর্তমান interlace-frames.py স্ক্রিপ্ট আপনাকে animation_fps তথ্য ছবিতে সেভ করার অনুমতি দেয়। Android-এর আগের ভার্সনে animation_fps আপনাকে নিজেই সেট করতে হত।

ভেরিয়েবল animation_fps সেট করতে, আপনার সাবক্লাসে ScreenRecoveryUI::Init() ফাংশন ওভাররাইড করুন। ভ্যালু সেট করুন, তারপর শুরু করার প্রসেস সম্পূর্ণ করতে parent Init() ফাংশন কল করুন। ডিফল্ট ভ্যালু (২০ FPS) ডিফল্ট রিকভারি ইমেজের সাথে সম্পর্কিত; এইসব ইমেজ ব্যবহার করার সময় আপনাকে Init() ফাংশন দিতে হবে না। ছবির ব্যাপারে বিস্তারিত জানতে, রিকভারি UI ছবি দেখুন।

ডিভাইস ক্লাস

RecoveryUI প্রয়োগ করার পরে, আপনার ডিভাইস ক্লাস (বিল্ট-ইন ডিভাইস ক্লাস থেকে সাবক্লাস করা) সংজ্ঞায়িত করুন। এটি আপনার UI ক্লাসের একটি সিঙ্গেল ইনস্ট্যান্স তৈরি করবে এবং GetUI() ফাংশন থেকে সেটি রিটার্ন করবে:

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

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

    RecoveryUI* GetUI() { return ui; }

StartRecovery

UI ইনিশিয়ালাইজ করা এবং আর্গুমেন্ট পার্স করার পরে, কিন্তু কোনও অ্যাকশন নেওয়ার আগে, রিকভারি শুরু হওয়ার সময় 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;
    }

এই পদ্ধতিতে একটি কী কোড (যা আগে UI অবজেক্টের CheckKey() পদ্ধতির মাধ্যমে প্রসেস ও এনকিউ করা হয়েছে) এবং মেনু/টেক্সট লগ দৃশ্যমানতার বর্তমান অবস্থা নেওয়া হয়। রিটার্ন ভ্যালু হল একটি পূর্ণসংখ্যা। মানটি ০ বা তার বেশি হলে, সেটি মেনু আইটেমের পজিশন হিসেবে ধরা হয়, যা সঙ্গে সঙ্গে ইনভোক করা হয় (নিচে দেওয়া InvokeMenuItem() পদ্ধতি দেখুন)। অন্যথায়, এটি নিম্নলিখিত পূর্বনির্ধারিত ধ্রুবকগুলির মধ্যে একটি হতে পারে:

  • kHighlightUp. মেনু হাইলাইটকে আগের আইটেমে নিয়ে যান
  • kHighlightDown. মেনু হাইলাইট করে পরের আইটেমে যান
  • kInvokeItem. বর্তমানে হাইলাইট করা আইটেমটি ইনভোক করা
  • kNoAction. এই কী প্রেস করলে কিছু হবে না

দৃশ্যমান আর্গুমেন্ট থেকে বোঝা যায় যে মেনু দৃশ্যমান না হলেও HandleMenuKey() কল করা হয়। CheckKey()-এর মতো নয়, এটি কল করা হয় না যখন রিকভারি কিছু করছে যেমন ডেটা ওয়াইপ করা বা প্যাকেজ ইনস্টল করা—এটি শুধু তখনই কল করা হয় যখন রিকভারি আইডল থাকে এবং ইনপুটের জন্য অপেক্ষা করে।

ট্র্যাকবল মেকানিজম

আপনার ডিভাইসে ট্র্যাকবল-এর মতো ইনপুট মেকানিজম থাকলে (EV_REL এবং কোড REL_Y সহ ইনপুট ইভেন্ট তৈরি করে), অ্যাকাউন্ট ফিরিয়ে আনা ফিচার KEY_UP এবং KEY_DOWN কীপ্রেস সিন্থেসাইজ করে, যখনই ট্র্যাকবল-এর মতো ইনপুট ডিভাইস Y অ্যাক্সিসে মোশন রিপোর্ট করে। আপনাকে যা করতে হবে তা হল KEY_UP এবং KEY_DOWN ইভেন্টকে মেনু অ্যাকশনের সাথে ম্যাপ করা। এই ম্যাপিং CheckKey()-এর জন্য হয় না, তাই আপনি রিবুট বা ডিসপ্লে টগল করার ট্রিগার হিসেবে ট্র্যাকবল মোশন ব্যবহার করতে পারবেন না।

সংশোধক কীগুলি

মডিফায়ার হিসেবে 'কী' প্রেস করে রাখা হয়েছে কিনা তা চেক করতে, আপনার নিজস্ব UI অবজেক্টের 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

এরপরে, GetMenuItems()-এর মাধ্যমে রিটার্ন করা আইটেমের অ্যারেতে InvokeMenuItem() পদ্ধতি প্রদান করুন যা অ্যাকশনের সাথে পূর্ণসংখ্যা পজিশন ম্যাপ করে। টার্ডিস উদাহরণে আইটেমের অ্যারের জন্য, এগুলি ব্যবহার করুন:

   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. কিছুই করার প্রয়োজন নেই।
  • রি-বুট করুন। রিকভারি থেকে বেরিয়ে এসে ডিভাইসটি সাধারণভাবে রিবুট করুন।
  • APPLY_EXT, APPLY_CACHE, APPLY_ADB_SIDELOAD. বিভিন্ন জায়গা থেকে আপডেট প্যাকেজ ইনস্টল করুন। আরও বিবরণের জন্য, সাইডলোডিং দেখুন।
  • WIPE_CACHE. শুধুমাত্র ক্যাশে পার্টিশন রিফর্ম্যাট করুন। এটি তুলনামূলকভাবে ক্ষতিকর নয় বলে কনফার্ম করার প্রয়োজন নেই।
  • WIPE_DATA. ব্যবহারকারীর ডেটা ও ক্যাশে পার্টিশন আবার ফর্ম্যাট করুন, এটি ফ্যাক্টরি ডেটা রিসেট হিসেবেও পরিচিত। এগিয়ে যাওয়ার আগে ব্যবহারকারীকে এই অ্যাকশন কনফার্ম করতে বলা হয়।

শেষ পদ্ধতি, WipeData(), হল ঐচ্ছিক এবং ডেটা মুছে দেওয়া অপারেশন শুরু করা হলে (মেনুর মাধ্যমে রিকভারি থেকে অথবা ব্যবহারকারী যখন মূল সিস্টেম থেকে ফ্যাক্টরি ডেটা রিসেট করার বিকল্প বেছে নেন) এটি কল করা হয়। ব্যবহারকারীর ডেটা ও ক্যাশে পার্টিশন মুছে দেওয়ার আগে এই পদ্ধতি কল করা হয়। আপনার ডিভাইস যদি এই দুটি পার্টিশন ছাড়া অন্য কোথাও ব্যবহারকারীর ডেটা সেভ করে থাকে, তাহলে আপনাকে এখান থেকে সেটি মুছে দিতে হবে। সফল হলে আপনাকে ০ এবং ব্যর্থ হলে অন্য মান রিটার্ন করতে হবে, যদিও বর্তমানে রিটার্ন ভ্যালু উপেক্ষা করা হয়। আপনি সাকসেস বা ব্যর্থতা যাই রিটার্ন করুন না কেন, ব্যবহারকারীর ডেটা ও ক্যাশে পার্টিশন ওয়াইপ হয়ে যাবে।

   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

রিকভারি UI ছবি

অ্যাকাউন্ট ফিরিয়ে আনার ইউজার ইন্টারফেসে ছবি থাকে। সাধারণত, ব্যবহারকারীকে UI-এর সাথে ইন্টার‍্যাক্ট করতে হয় না: সাধারণ আপডেটের সময়, ফোন রিকভারি মোডে বুট হয়, ইনস্টলেশন প্রোগ্রেস বার সম্পূর্ণ হয়, এবং ব্যবহারকারীর ইনপুট ছাড়াই নতুন সিস্টেমে বুট হয়। সিস্টেম আপডেট সংক্রান্ত সমস্যা হলে, ব্যবহারকারী শুধুমাত্র গ্রাহক পরিষেবায় কল করতে পারবেন।

শুধুমাত্র ছবি দেখানো ইন্টারফেসের ক্ষেত্রে লোকালাইজেশনের প্রয়োজন হয় না। তবে, Android 5.0 থেকে, আপডেটটি ছবির সাথে টেক্সটের একটি স্ট্রিং (যেমন, "সিস্টেম আপডেট ইনস্টল করা হচ্ছে...") দেখাতে পারে। বিস্তারিত জানতে, স্থানীয়করণ করা রিকভারি টেক্সট দেখুন।

Android 5.0 ও তার পরবর্তী ভার্সন

Android 5.0 এবং তার পরবর্তী ভার্সনের রিকভারি UI দুটি প্রধান ছবি ব্যবহার করে: সমস্যা ছবি এবং ইনস্টল করা হচ্ছে অ্যানিমেশন।

OTA সমস্যার সময় দেখানো ছবি

ছবি ১. icon_error.png

OTA ইনস্টল করার সময় দেখানো ছবি

ছবি ২. icon_installing.png

ইনস্টল করার অ্যানিমেশনকে একটি PNG ছবি হিসেবে দেখানো হয়, যার মধ্যে অ্যানিমেশনের আলাদা আলাদা ফ্রেম সারিতে ইন্টারলেস করা থাকে (এই কারণে ২ নম্বর ছবিটিকে চ্যাপ্টা মনে হয়)। যেমন, 200x200 সাইজের সাতটি ফ্রেমের অ্যানিমেশনের জন্য, একটি 200x1400 সাইজের ছবি তৈরি করুন যেখানে প্রথম ফ্রেমটি হল সারি 0, 7, 14, 21, ...; দ্বিতীয় ফ্রেমটি হল সারি 1, 8, 15, 22, ...; ইত্যাদি। একত্রিত ছবিতে একটি টেক্সট চ্যাঙ্ক থাকে যা অ্যানিমেশন ফ্রেমের সংখ্যা এবং প্রতি সেকেন্ডে ফ্রেমের সংখ্যা (FPS) নির্দেশ করে। bootable/recovery/interlace-frames.py টুলটি ইনপুট ফ্রেমের একটি সেট নেয় এবং রিকভারির জন্য প্রয়োজনীয় কম্পোজিট ছবিতে সেগুলিকে একত্রিত করে।

ডিফল্ট ছবি বিভিন্ন ঘনত্বে উপলভ্য এবং সেগুলি bootable/recovery/res-$DENSITY/images-এ (যেমন, bootable/recovery/res-hdpi/images) থাকে। ইনস্টলেশনের সময় স্ট্যাটিক ছবি ব্যবহার করতে, আপনাকে শুধু icon_installing.png ছবি দিতে হবে এবং অ্যানিমেশনে ফ্রেমের সংখ্যা ০-তে সেট করতে হবে (ত্রুটি আইকন অ্যানিমেট করা হয় না; এটি সবসময় স্ট্যাটিক ছবি হয়)।

Android 4.x এবং আগের ভার্সন

Android 4.x এবং তার আগের ভার্সনের রিকভারি UI-তে error ছবি (উপরে দেখানো হয়েছে) এবং ইনস্টল করা হচ্ছে অ্যানিমেশন সহ একাধিক ওভারলে ছবি ব্যবহার করা হয়:

OTA ইনস্টল করার সময় দেখানো ছবি

ছবি ৩. icon_installing.png

প্রথম
ওভারলে হিসেবে দেখানো ছবি

ছবি ৪. icon-installing_overlay01.png

ছবিটি সপ্তম
ওভারলে হিসেবে দেখানো হয়েছে

ছবি ৫. icon_installing_overlay07.png

ইনস্টলেশনের সময়, icon_installing.png ছবি এঁকে অন-স্ক্রিন ডিসপ্লে তৈরি করা হয়, তারপর সঠিক অফসেটে এর উপরে ওভারলে ফ্রেমের একটি আঁকা হয়। এখানে, বেস ছবির উপরে ওভারলে কোথায় প্লেস করা হয়েছে তা হাইলাইট করতে একটি লাল বক্স সুপারইম্পোজ করা হয়েছে:

কম্পোজিট ছবি
ইনস্টল প্লাস ফার্স্ট ওভারলে

ছবি ৬. অ্যানিমেশন ফ্রেম ১ ইনস্টল করা হচ্ছে (icon_installing.png + icon_installing_overlay01.png)

ইনস্টল প্লাস সেভেন্থ ওভারলে
কম্পোজিট ছবি

ছবি ৭. অ্যানিমেশন ফ্রেম ৭ ইনস্টল করা হচ্ছে (icon_installing.png + icon_installing_overlay07.png)

আগে থেকে যা আছে তার উপরে শুধুমাত্র পরবর্তী ওভারলে ছবি এঁকে পরবর্তী ফ্রেম দেখানো হয়; বেস ছবি আবার আঁকা হয় না।

অ্যানিমেশনে ফ্রেমের সংখ্যা, কাঙ্ক্ষিত স্পিড এবং বেসের সাপেক্ষে ওভারলের x- ও y-অফসেট ScreenRecoveryUI ক্লাসের মেম্বার ভেরিয়েবল দ্বারা সেট করা হয়। ডিফল্ট ছবির পরিবর্তে কাস্টম ছবি ব্যবহার করার সময়, আপনার কাস্টম ছবির জন্য এইসব ভ্যালু পরিবর্তন করতে, আপনার সাবক্লাসে Init() পদ্ধতি ওভাররাইড করুন (বিস্তারিত জানতে, ScreenRecoveryUI দেখুন)। স্ক্রিপ্ট bootable/recovery/make-overlay.py রিকভারির জন্য প্রয়োজনীয় "বেস ছবি + ওভারলে ছবি" ফর্মের মধ্যে একগুচ্ছ ছবি ফ্রেম কনভার্ট করতে সাহায্য করতে পারে, এর মধ্যে প্রয়োজনীয় অফসেট কম্পিউট করাও অন্তর্ভুক্ত।

ডিফল্ট ছবি bootable/recovery/res/images-এ থাকে। ইনস্টলেশন চলাকালীন স্ট্যাটিক ছবি ব্যবহার করতে, আপনাকে শুধু icon_installing.png ছবি দিতে হবে এবং অ্যানিমেশনে ফ্রেমের সংখ্যা ০ হিসেবে সেট করতে হবে (ভুল সংক্রান্ত আইকন অ্যানিমেটেড নয়; এটি সবসময় স্ট্যাটিক ছবি)।

স্থানীয় ভাষায় লেখা অ্যাকাউন্ট ফিরিয়ে আনা সংক্রান্ত টেক্সট

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

প্রোগ্রেস বার তৈরি করতে ভর্তি ছবির বাঁদিকের শেষ প্রান্তটি খালি ছবির ডানদিকের শেষ প্রান্তের পাশে দেখানো হয়। দুটি ছবির মধ্যেকার সীমানার পজিশন পরিবর্তন করে প্রগ্রেস দেখানো হয়। যেমন, ইনপুট ইমেজের উপরের পেয়ারের সাথে, ডিসপ্লে:

১% প্রগ্রেস বার

ছবি ১১. প্রগ্রেস বার ১%>

প্রোগ্রেস বার ১০% সম্পূর্ণ হয়েছে

ছবি ১২. প্রগ্রেস বার ১০%

৫০% পর্যন্ত প্রগ্রেস বার

ছবি ১৩. প্রগ্রেস বার ৫০%

আপনি এইসব ছবির ডিভাইস-নির্দিষ্ট ভার্সন (এই উদাহরণে) device/yoyodyne/tardis/recovery/res/images -এ রেখে দিতে পারেন। ফাইলের নাম উপরে তালিকাভুক্ত নামের সাথে মিলতে হবে; সেই ডিরেক্টরিতে কোনও ফাইল পাওয়া গেলে, বিল্ড সিস্টেম সংশ্লিষ্ট ডিফল্ট ছবির পরিবর্তে এটি ব্যবহার করে। শুধুমাত্র RGB বা RGBA ফর্ম্যাটে ৮-বিট কালার ডেপথ সহ PNG কাজ করে।

মনে রাখবেন: Android 5.x-এ, রিকভারি যদি লোকেল জানে এবং সেটি যদি বামদিক-থেকে-ডানদিকে (RTL) লেখা ভাষা (আরবি, হিব্রু ইত্যাদি) হয়, তাহলে প্রগ্রেস বার ডানদিক থেকে বামদিকে পূরণ হয়।

স্ক্রিন ছাড়া ডিভাইস

সব Android ডিভাইসে স্ক্রিন থাকে না। আপনার ডিভাইস যদি হেডলেস অ্যাপ্লায়েন্স হয় বা এতে শুধুমাত্র অডিও ইন্টারফেস থাকে, তাহলে আপনাকে রিকভারি UI-এর আরও ব্যাপক কাস্টমাইজেশন করতে হতে পারে। ScreenRecoveryUI-এর সাবক্লাস তৈরি করার পরিবর্তে, সরাসরি এর পেরেন্ট ক্লাস RecoveryUI-এর সাবক্লাস তৈরি করুন।

RecoveryUI-তে "ডিসপ্লে টগল করা," "প্রোগ্রেস বার আপডেট করা," "মেনু দেখানো," "মেনু সিলেকশন পরিবর্তন করা" ইত্যাদির মতো লোয়ার-লেভেল UI অপারেশন ম্যানেজ করার পদ্ধতি আছে। আপনার ডিভাইসের জন্য উপযুক্ত ইন্টারফেস প্রদান করতে আপনি এগুলি ওভাররাইড করতে পারবেন। আপনার ডিভাইসে LED থাকতে পারে যেখানে আপনি স্ট্যাটাস দেখানোর জন্য বিভিন্ন রঙ বা ফ্ল্যাশিংয়ের প্যাটার্ন ব্যবহার করতে পারেন অথবা অডিও চালাতে পারেন। (সম্ভবত আপনি কোনও মেনু বা "টেক্সট ডিসপ্লে" মোড একেবারেই সাপোর্ট করতে চান না; আপনি 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() কল করার দায়িত্ব আপনার।

ধরা যাক, ফাংশনের দুটি আর্গুমেন্ট প্রয়োজন: স্ট্রিং-ভ্যালুযুক্ত কী এবং ব্লব-ভ্যালুযুক্ত ছবি। আপনি এই ধরনের যুক্তি পড়তে পারেন:

   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-এর মাধ্যমে নির্দেশিত যেকোনও ডেটার মালিকানা কলারের কাছে থাকে।

এই ক্ষেত্রে, আপনি সফল হয়েছেন কিনা তা বোঝাতে একটি true বা false ভ্যালু রিটার্ন করতে চান। মনে রাখবেন যে প্রথা অনুযায়ী, ফাঁকা স্ট্রিং ফলস এবং অন্য সব স্ট্রিং ট্রু হিসেবে বিবেচিত হয়। আপনাকে অবশ্যই একটি ভ্যালু অবজেক্ট ম্যালক করতে হবে, যার সাথে কনস্ট্যান্ট স্ট্রিংয়ের একটি ম্যালক করা কপি থাকবে, কারণ কলার দুটিকেই 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);
}

আপনার কোড দিয়ে স্ট্যাটিক লাইব্রেরি তৈরি করতে, আপনি এখন makefile কনফিগার করতে পারবেন। (এটি সেই একই makefile যা আগের বিভাগে রিকভারি UI কাস্টমাইজ করতে ব্যবহার করা হয়েছে; আপনার ডিভাইসে এখানে সংজ্ঞায়িত করা স্ট্যাটিক লাইব্রেরি থাকতে পারে।)

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 ফাংশন না থাকে), তাহলে আপনি সেগুলিকে updater-এর সাথে লিঙ্ক করার জন্য 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 +=

আপনার OTA প্যাকেজের আপডেটার স্ক্রিপ্ট এখন অন্য কোনও স্ক্রিপ্টের মতো আপনার ফাংশন কল করতে পারবে। আপনার টার্ডিস ডিভাইস রিপ্রোগ্রাম করতে, আপডেট স্ক্রিপ্টে এগুলি থাকতে পারে: tardis.reprogram("the-key", package_extract_file("tardis-image.dat")) . এটি বিল্ট-ইন ফাংশনের সিঙ্গেল-আর্গুমেন্ট ভার্সন package_extract_file() ব্যবহার করে, যা নতুন এক্সটেনশন ফাংশনের দ্বিতীয় আর্গুমেন্ট তৈরি করতে আপডেট প্যাকেজ থেকে এক্সট্র্যাক্ট করা ফাইলের কন্টেন্টকে একটি ব্লব হিসেবে রিটার্ন করে।

OTA প্যাকেজ জেনারেশন

শেষ কম্পোনেন্ট হল 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

ঐতিহাসিক কারণে এগুলিকে রেডিও ফাইল বলা হয়; এগুলির সাথে ডিভাইসের রেডিওর (যদি থাকে) কোনও সম্পর্ক নাও থাকতে পারে। এগুলি হল ডেটার অস্বচ্ছ ব্লব যা বিল্ড সিস্টেম OTA জেনারেশন টুল দ্বারা ব্যবহৃত টার্গেট-ফাইল .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"));""")

ইনক্রিমেন্টাল OTA প্যাকেজ তৈরি করার বিষয়টি একটি আলাদা ফাংশন হ্যান্ডেল করে। এই উদাহরণে, ধরে নিন যে 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()
সম্পূর্ণ OTA জেনারেট করার প্রসেস শুরু হওয়ার কাছাকাছি সময়ে কল করা হয়েছে। ডিভাইসের বর্তমান অবস্থা সম্পর্কে অ্যাসারশন ইমিট করার জন্য এটি একটি ভাল জায়গা। ডিভাইসে পরিবর্তন করে এমন স্ক্রিপ্ট কমান্ড এমিট করবেন না।
FullOTA_InstallBegin()
ডিভাইসের অবস্থা সম্পর্কে সমস্ত অ্যাসারশন পাস করার পরে কিন্তু কোনও পরিবর্তন করার আগে কল করা হয়। ডিভাইসে অন্য কিছু পরিবর্তন করার আগে ডিভাইসের নির্দিষ্ট আপডেট চালানোর জন্য আপনি কমান্ড দিতে পারেন।
FullOTA_InstallEnd()
স্ক্রিপ্ট জেনারেশনের শেষে কল করা হয়, বুট ও সিস্টেম পার্টিশন আপডেট করার জন্য স্ক্রিপ্ট কমান্ড এমিট করার পরে। এছাড়াও, আপনি ডিভাইস-নির্দিষ্ট আপডেটের জন্য অতিরিক্ত কমান্ড ইমিট করতে পারেন।
IncrementalOTA_Assertions()
FullOTA_Assertions()-এর মতো, তবে ইনক্রিমেন্টাল আপডেট প্যাকেজ তৈরি করার সময় কল করা হয়।
IncrementalOTA_VerifyBegin()
ডিভাইসের স্টেট সম্পর্কে সব অ্যাসারশন পাস করার পরে কিন্তু কোনও পরিবর্তন করার আগে কল করা হয়। ডিভাইসে অন্য কিছু পরিবর্তন করার আগে ডিভাইসের নির্দিষ্ট আপডেট চালানোর জন্য আপনি কমান্ড দিতে পারেন ।
IncrementalOTA_VerifyEnd()
যাচাইকরণ পর্বের শেষে কল করা হয়, যখন স্ক্রিপ্ট নিশ্চিত করে যে এটি যে ফাইলগুলি অ্যাক্সেস করতে চলেছে সেগুলিতে প্রত্যাশিত কন্টেন্ট আছে। এই পর্যায়ে ডিভাইসে কোনও পরিবর্তন করা হয়নি। এছাড়াও, আপনি অতিরিক্ত ডিভাইস-নির্দিষ্ট যাচাইকরণের জন্য কোড এমিট করতে পারবেন।
IncrementalOTA_InstallBegin()
প্যাচ করা হবে এমন ফাইল প্রত্যাশিত আগের অবস্থায় আছে কিনা তা যাচাই করার পরে কিন্তু কোনও পরিবর্তন করার আগে কল করা হয়েছে। ডিভাইসে অন্য কিছু পরিবর্তন করার আগে ডিভাইস-নির্দিষ্ট আপডেট চালানোর জন্য আপনি কমান্ড দিতে পারেন।
IncrementalOTA_InstallEnd()
সম্পূর্ণ OTA প্যাকেজের মতোই, স্ক্রিপ্ট জেনারেশনের শেষে এটি কল করা হয়, বুট ও সিস্টেম পার্টিশন আপডেট করার জন্য স্ক্রিপ্ট কমান্ড নির্গত হওয়ার পরে। এছাড়াও, আপনি ডিভাইস-নির্দিষ্ট আপডেটের জন্য অতিরিক্ত কমান্ড দিতে পারেন।

মনে রাখবেন: ডিভাইসে পাওয়ার না থাকলে, OTA ইনস্টলেশন আবার শুরু থেকে রিস্টার্ট হতে পারে। যেসব ডিভাইসে এইসব কমান্ড আগে থেকেই রান করা হয়েছে, সম্পূর্ণ বা আংশিকভাবে, সেগুলির সাথে মানিয়ে নেওয়ার জন্য প্রস্তুত থাকুন।

তথ্য অবজেক্টে ফাংশন পাস করা

একটিমাত্র তথ্য অবজেক্টে ফাংশন পাস করুন যাতে বিভিন্ন উপযোগী আইটেম থাকে:

  • info.input_zip. (শুধুমাত্র সম্পূর্ণ OTA) ইনপুট target-files .zip-এর জন্য zipfile.ZipFile অবজেক্ট।
  • info.source_zip. (শুধুমাত্র ইনক্রিমেন্টাল OTA) সোর্স টার্গেট-ফাইল .zip-এর জন্য zipfile.ZipFile অবজেক্ট (ইনক্রিমেন্টাল প্যাকেজ ইনস্টল করার সময় ডিভাইসে আগে থেকেই থাকা বিল্ড)।
  • info.target_zip. (শুধুমাত্র ইনক্রিমেন্টাল OTA) টার্গেট target-files .zip-এর জন্য zipfile.ZipFile অবজেক্ট (ইনক্রিমেন্টাল প্যাকেজ ডিভাইসে যে বিল্ড রাখে)।
  • info.output_zip. প্যাকেজ তৈরি করা হচ্ছে; লেখার জন্য একটি zipfile.ZipFile অবজেক্ট খোলা হয়েছে। প্যাকেজে ফাইল যোগ করতে common.ZipWriteStr(info.output_zip, filename, data) ব্যবহার করুন।
  • info.script. স্ক্রিপ্ট অবজেক্ট যাতে আপনি কমান্ড যোগ করতে পারবেন। স্ক্রিপ্টে টেক্সট আউটপুট করতে info.script.AppendExtra(script_text) কল করুন। আউটপুট টেক্সটের শেষে সেমিকোলন আছে কিনা দেখে নিন, যাতে পরে কমান্ডে কোনও সমস্যা না হয়।

info অবজেক্টের ব্যাপারে বিস্তারিত জানতে, 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 ডিভাইস তৈরি করার সময়, target-files .zip ফাইলে (META/releasetools.py ) releasetools.py স্ক্রিপ্ট অন্তর্ভুক্ত করা হয়।

আপনি রিলিজ টুল (img_from_target_files বা ota_from_target_files) রান করলে, target-files .zip-এ releasetools.py স্ক্রিপ্ট, যদি থাকে, Android সোর্স ট্রি থেকে নেওয়া স্ক্রিপ্টের চেয়ে বেশি পছন্দ করা হয়। এছাড়াও, আপনি -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 ব্যবহার করে USB-এর মাধ্যমে সেগুলি লোড করা।

প্রতিটি সাইডলোড মেকানিজমকে ইনভোক করতে, আপনার ডিভাইসের Device::InvokeMenuItem() পদ্ধতি BuiltinAction-এর নিম্নলিখিত ভ্যালু রিটার্ন করতে পারে:

  • APPLY_EXT. এক্সটার্নাল স্টোরেজ ( /sdcard ডিরেক্টরি) থেকে আপডেট প্যাকেজ সাইডলোড করুন। আপনার recovery.fstab-এ অবশ্যই /sdcard মাউন্ট পয়েন্ট উল্লেখ করতে হবে। এটি /data-এর (বা এই ধরনের কিছু মেকানিজম) সাথে সিমলিঙ্ক করা এসডি কার্ড এমুলেট করে এমন ডিভাইসে ব্যবহার করা যায় না। /data সাধারণত রিকভার করার জন্য উপলভ্য থাকে না কারণ এটি এনক্রিপ্ট করা থাকতে পারে। রিকভারি UI /sdcard-এ .zip ফাইলের একটি মেনু দেখায় এবং ব্যবহারকারীকে একটি বেছে নেওয়ার অনুমতি দেয়।
  • APPLY_CACHE. /sdcard থেকে প্যাকেজ লোড করার মতোই, তবে এর পরিবর্তে /cache ডিরেক্টরি (যা রিকভারির জন্য সবসময় উপলভ্য থাকে) ব্যবহার করা হয়। সাধারণ সিস্টেম থেকে, /cache শুধুমাত্র বিশেষ সুবিধা প্রাপ্ত ব্যবহারকারীদের দ্বারা লেখা যায়, এবং ডিভাইস বুট করার উপযুক্ত না হলে, /cache ডিরেক্টরি একেবারেই লেখা যায় না (যা এই মেকানিজমকে সীমিত ইউটিলিটি করে তোলে)।
  • APPLY_ADB_SIDELOAD ব্যবহারকারীকে USB কেবেল ও adb ডেভেলপমেন্ট টুলের মাধ্যমে ডিভাইসে প্যাকেজ পাঠানোর অনুমতি দেয়। এই মেকানিজম চালু হলে, রিকভারি নিজের adbd ডেমনের একটি মিনি ভার্সন চালু করে যাতে কানেক্ট করা হোস্ট কম্পিউটারে adb এটির সাথে কথা বলতে পারে। এই মিনি ভার্সনে শুধু একটি কমান্ড কাজ করে: adb sideload filename. হোস্ট মেশিন থেকে ডিভাইসে নাম করা ফাইল পাঠানো হয়, ডিভাইস সেটি যাচাই করে এবং এভাবে ইনস্টল করে যেন সেটি লোকাল স্টোরেজে ছিল।

কয়েকটি বিষয় মনে রাখতে হবে:

  • শুধুমাত্র USB ট্রান্সপোর্ট কাজ করে।
  • আপনার রিকভারি যদি adbd স্বাভাবিকভাবে রান করে (সাধারণত userdebug এবং eng বিল্ডের ক্ষেত্রে সত্য), তাহলে ডিভাইস adb সাইডলোড মোডে থাকাকালীন সেটি বন্ধ হয়ে যাবে এবং adb সাইডলোড প্যাকেজ রিসিভ করা শেষ করলে রিস্টার্ট হবে। adb সাইডলোড মোডে থাকাকালীন, sideload ছাড়া অন্য কোনও adb কমান্ড কাজ করে না ( logcat, reboot, push, pull , shell, ইত্যাদি সব ব্যর্থ হয়)।
  • আপনি ডিভাইসে adb সাইডলোড মোড থেকে বেরোতে পারবেন না। বাতিল করতে, আপনি প্যাকেজ হিসেবে /dev/null (বা অন্য কিছু যা সঠিক প্যাকেজ নয়) পাঠাতে পারেন এবং তারপর ডিভাইস এটি যাচাই করতে পারবে না এবং ইনস্টলেশন পদ্ধতি বন্ধ করে দেবে। RecoveryUI ইমপ্লিমেন্টেশনের CheckKey() মেথড কীপ্রেসের জন্য কল করা চালিয়ে যাবে, তাই আপনি এমন একটি কী সিকোয়েন্স প্রদান করতে পারবেন যা ডিভাইসটি রিবুট করে এবং adb সাইডলোড মোডে কাজ করে।