کد ویژه دستگاه

سیستم بازیابی شامل چندین قلاب برای درج کد مختص دستگاه است تا به‌روزرسانی‌های OTA بتوانند بخش‌های دیگری از دستگاه را نیز به‌روز کنند (مثلاً پردازنده رادیویی یا باند پایه).

بخش‌ها و نمونه‌های زیر دستگاه tardis تولیدشده توسط فروشنده yoyodyne را سفارشی‌سازی می‌کنند.

نقشه پارتیشن

از Android 2.3، این پلاتفرم از دستگاه‌های فلش eMMc و سیستم فایل ext4 که در آن دستگاه‌ها اجرا می‌شود پشتیبانی می‌کند. همچنین از دستگاه‌های حافظه فلش فناوری حافظه (MTD) و سیستم فایل yaffs2 در نسخه‌های قدیمی‌تر پشتیبانی می‌کند.

فایل نقشه پارتیشن با TARGET_RECOVERY_FSTAB مشخص می‌شود؛ این فایل هم توسط باینری بازیابی و هم توسط ابزارهای ساخت بسته استفاده می‌شود. می‌توانید نام فایل نقشه را در TARGET_RECOVERY_FSTAB در BoardConfig.mk مشخص کنید.

فایل نقشه پارتیشن نمونه ممکن است به این شکل باشد:

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
سیستم فایل yaffs2 در بالای دستگاه فلاش MTD. «دستگاه» باید نام پارتیشن MTD باشد و باید در /proc/mtd نشان داده شود.
mtd
یک پارتیشن MTD خام، که برای پارتیشن‌های قابل‌راه‌اندازی مانند راه‌اندازی و بازیابی استفاده می‌شود. ‫MTD درواقع نصب نشده است، اما از نقطه نصب به‌عنوان کلیدی برای مکان‌یابی افراز استفاده می‌شود. «device» باید نام پارتیشن MTD در /proc/mtd باشد.
ext4
سیستم فایل ext4 در بالای دستگاه حافظه فلش eMMc. «device» باید مسیر دستگاه بلوک باشد.
emmc
دستگاه بلوک eMMc خام، که برای پارتیشن‌های قابل‌راه‌اندازی مثل راه‌اندازی و بازیابی استفاده می‌شود. مشابه با نوع mtd،‏ eMMc هرگز واقعاً نصب نمی‌شود، اما رشته نقطه نصب برای مکان‌یابی دستگاه در جدول استفاده می‌شود.
vfat
سیستم فایل FAT در بالای دستگاه بلوک، معمولاً برای فضای ذخیره‌سازی خارجی مانند کارت SD. دستگاه دستگاه بلوک است؛ device2 دستگاه بلوک دومی است که سیستم تلاش می‌کند درصورت ناموفق بودن سوار کردن دستگاه اصلی، آن را سوار کند (برای سازگاری با کارت‌های SD که ممکن است با جدول پارتیشن قالب‌بندی شده باشند یا نشده باشند).

همه پارتیشن‌ها باید در دایرکتوری ریشه نصب شوند (یعنی مقدار نقطه نصب باید با خط مورب شروع شود و خط مورب دیگری نداشته باشد). این محدودیت فقط برای نصب سیستم‌های فایل در حالت بازیابی اعمال می‌شود؛ سیستم اصلی می‌تواند آن‌ها را در هر جایی نصب کند. دایرکتوری‌های /boot،‏ /recovery، و /misc باید از نوع خام باشند (mtd یا emmc)، درحالی‌که دایرکتوری‌های /system،‏ /data،‏ /cache، و /sdcard (درصورت دردسترس بودن) باید از نوع سیستم فایل باشند (yaffs2،‏ ext4، یا vfat).

از Android 3.0، فایل recovery.fstab یک فیلد اختیاری دیگر به‌نام options دریافت می‌کند. درحال‌حاضر تنها گزینه تعریف‌شده طول است که به شما امکان می‌دهد به‌طور صریح طول پارتیشن را مشخص کنید. این طول هنگام قالب‌بندی مجدد پارتیشن استفاده می‌شود (برای مثال، برای پارتیشن داده‌های کاربر درطول عملیات پاک‌سازی داده‌ها/بازنشانی کارخانه‌ای، یا برای پارتیشن سیستم درطول نصب بسته کامل OTA). اگر مقدار طول منفی باشد، اندازه قالب‌بندی با افزودن مقدار طول به اندازه واقعی پارتیشن به‌دست می‌آید. برای مثال، تنظیم «length=-16384» یعنی وقتی آن پارتیشن مجدداً قالب‌بندی می‌شود، ۱۶ هزار آخر آن پارتیشن بازنویسی نخواهد شد. این امر از ویژگی‌هایی مانند رمزگذاری بخش userdata (که در آن فراداده رمزگذاری در انتهای بخشی که نباید رونویسی شود ذخیره می‌شود) پشتیبانی می‌کند.

توجه: فیلدهای device2 و options اختیاری هستند و باعث ایجاد ابهام در تجزیه می‌شوند. اگر ورودی در فیلد چهارم خط با نویسه «/» شروع شود، ورودی دستگاه۲ درنظر گرفته می‌شود؛ اگر ورودی با نویسه «/» شروع نشود، فیلد گزینه‌ها درنظر گرفته می‌شود.

پویانمایی راه‌اندازی

تولیدکنندگان دستگاه می‌توانند پویانمایی نمایش‌داده‌شده هنگام راه‌اندازی دستگاه Android را سفارشی‌سازی کنند. برای انجام این کار، فایل zip .را بسازید که براساس مشخصات در قالب bootanimation سازمان‌دهی و مکان‌یابی شده باشد.

برای دستگاه‌های Android Things، می‌توانید فایل فشرده‌شده را در کنسول Android Things بارگذاری کنید تا تصاویر در محصول انتخاب‌شده گنجانده شود.

توجه: این تصاویر باید با دستورالعمل‌های نمانام Android مطابقت داشته باشند. برای دستورالعمل‌های نمانام، به بخش Android در مرکز بازاریابی شریک مراجعه کنید.

میانای کاربر بازیابی

برای پشتیبانی از دستگاه‌هایی با سخت‌افزار دردسترس متفاوت (دکمه‌های فیزیکی، 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 };

توجه: خطوط طولانی کوتاه می‌شوند (درهم‌پیچیده نمی‌شوند)، بنابراین عرض صفحه‌نمایش دستگاهتان را درنظر داشته باشید.

سفارشی‌سازی کلید بررسی

سپس، پیاده‌سازی RecoveryUI دستگاهتان را تعریف کنید. در این مثال فرض می‌شود که دستگاه tardis صفحه‌نمایش دارد، بنابراین می‌توانید از پیاده‌سازی ScreenRecoveryUI داخلی ارث ببرید (دستورالعمل‌های دستگاه‌های بدون صفحه‌نمایش را ببینید.) تنها عملکردی که می‌توان از 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() صرف‌نظر از اینکه در بقیه بازیابی چه اتفاقی می‌افتد فراخوانی می‌شود: وقتی منو خاموش است، وقتی روشن است، درطول نصب بسته، درطول پاک کردن userdata، و غیره. می‌تواند یکی از چهار ثابت را برگرداند:

  • روشن/خاموش کردن. نمایش منو و/یا گزارش نوشتاری را روشن یا خاموش کنید
  • REBOOT. دستگاه فوراً بازراه‌اندازی شود
  • نادیده گرفتن. نادیده گرفتن این فشار کلید
  • ENQUEUE. این فشار کلید را در صف قرار دهید تا به‌صورت هم‌زمان مصرف شود (یعنی توسط سیستم منو بازیابی اگر نمایشگر فعال باشد)

هر بار که رویداد کلید پایین با رویداد کلید بالا برای همان کلید دنبال می‌شود، CheckKey() فراخوانده می‌شود. (توالی رویدادهای A-پایین B-پایین B-بالا A-بالا فقط منجر به فراخوانی CheckKey(B) می‌شود.) CheckKey() می‌تواند IsKeyPressed() را فراخوانی کند تا متوجه شود کلیدهای دیگری پایین نگه داشته شده‌اند یا نه. (در توالی رویدادهای کلیدی بالا، اگر 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، می‌توانید متغیر animation_fps را برای کنترل سرعت پویانمایی‌ها در فریم در ثانیه (FPS) تنظیم کنید.

توجه: اسکریپت interlace-frames.py فعلی به شما امکان می‌دهد اطلاعات animation_fps را در خود تصویر ذخیره کنید. در نسخه‌های قبلی Android، لازم بود animation_fps را خودتان تنظیم کنید.

برای تنظیم متغیر animation_fps، تابع ScreenRecoveryUI::Init() را در زیرکلاس خود ملغی کنید. مقدار را تنظیم کنید، سپس تابع parent Init() را فراخوانی کنید تا مقداردهی اولیه تکمیل شود. مقدار پیش‌فرض (۲۰ فریم در ثانیه) با تصاویر بازیابی پیش‌فرض مطابقت دارد؛ هنگام استفاده از این تصاویر، نیازی به ارائه عملکرد Init() ندارید. برای جزئیات مربوط به تصاویر، به تصاویر واسط کاربر بازیابی مراجعه کنید.

کلاس دستگاه

پس‌از پیاده‌سازی RecoveryUI، کلاس دستگاهتان را تعریف کنید (زیرکلاس از کلاس داخلی Device). باید یک نمونه واحد از کلاس واسط کاربرتان ایجاد کند و آن را از تابع 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() ارائه دهید که فشار کلید و نمایان بودن منو فعلی را دریافت می‌کند و تصمیم می‌گیرد چه اقدامی انجام دهد:

   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() شیء رابط کاربری پردازش و در صف قرار گرفته است) و وضعیت فعلی رؤیت‌پذیری گزارش نوشتاری/منو را می‌گیرد. مقدار برگشتی عدد صحیح است. اگر مقدار ۰ یا بیشتر باشد، به‌عنوان موقعیت یک عنصر منو درنظر گرفته می‌شود که بلافاصله فراخوانی می‌شود (به روش 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
        }
        ...
    }

توجه: اگر قابل‌مشاهده نادرست باشد، برگرداندن مقادیر ویژه‌ای که منو را دستکاری می‌کنند (انتقال برجستگی، فراخوانی مورد برجسته) منطقی نیست زیرا کاربر نمی‌تواند برجستگی را ببیند. بااین‌حال، درصورت تمایل می‌توانید مقادیر را برگردانید.

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. هیچ کاری نکنید.
  • REBOOT. از بازیابی خارج شوید و دستگاه را به‌صورت عادی بازراه‌اندازی کنید.
  • 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() اضافه کنید که نمونه‌ای از کلاس 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 نسخه ۵.۰ و بالاتر

واسط کاربر بازیابی Android 5.0 و نسخه‌های بالاتر از دو تصویر اصلی استفاده می‌کند: تصویر خطا و پویانمایی نصب.

تصویر نشان‌داده‌شده درطول خطای ota

شکل ۱. icon_error.png

تصویر نشان‌داده‌شده درطول نصب OTA

شکل ۲. icon_installing.png

پویانمایی نصب به‌صورت یک تصویر PNG واحد با قاب‌های مختلف پویانمایی که به‌صورت ردیفی درهم‌آمیخته شده‌اند نشان داده می‌شود (به همین دلیل است که شکل ۲ فشرده به‌نظر می‌رسد). برای مثال، برای پویانمایی هفت‌قاب با ابعاد ۲۰۰x۲۰۰، یک تصویر ۲۰۰x۱۴۰۰ ایجاد کنید که قاب اول ردیف‌های ۰، ۷، ۱۴، ۲۱، و غیره را دربرگیرد؛ قاب دوم ردیف‌های ۱، ۸، ۱۵، ۲۲، و غیره را دربرگیرد؛ و غیره. تصویر ترکیبی شامل تکه نوشتاری است که تعداد قاب‌های پویانمایی و تعداد قاب در ثانیه (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 و نسخه‌های قدیمی‌تر از تصویر خطا (در بالا نشان داده شده است) و پویانمایی درحال نصب به‌علاوه چندین تصویر هم‌پوشان استفاده می‌کند:

تصویر نشان‌داده‌شده درطول نصب 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 رشته‌ای از نوشتار (مثلاً «درحال نصب به‌روزرسانی سیستم...») را همراه با تصویر نشان می‌دهد. وقتی سیستم اصلی در حالت بازیابی راه‌اندازی می‌شود، مکان فعلی کاربر را به‌عنوان گزینه خط فرمان به بازیابی منتقل می‌کند. برای هر پیامی که باید نمایش داده شود، بازیابی شامل تصویر ترکیبی دومی است که رشته‌های نوشتاری ازپیش پرداز زده‌شده برای آن پیام در هر منطقه زبانی دارد.

تصویر نمونه رشته‌های نوشتاری بازیابی:

تصویر نوشتار بازیابی

شکل ۸. نوشتار بومی‌سازی‌شده برای پیام‌های بازیابی

نوشتار بازیابی می‌تواند پیام‌های زیر را نمایش دهد:

  • درحال نصب به‌روزرسانی سیستم…
  • خطا!
  • درحال پاک کردن... (هنگام پاک‌سازی داده‌ها/بازنشانی کارخانه‌ای)
  • بدون فرمان (وقتی کاربر به‌صورت دستی وارد حالت بازیابی می‌شود)

برنامه Android در bootable/recovery/tools/recovery_l10n/ ترجمه‌های محلی پیام را ارائه می‌کند و تصویر ترکیبی را ایجاد می‌کند. برای جزئیات استفاده از این برنامه، به نظرات در bootable/recovery/tools/recovery_l10n/src/com/android/recovery_l10n/Main.java مراجعه کنید.

وقتی کاربر به‌صورت دستی به بازیابی راه‌اندازی می‌کند، ممکن است زبان محلی دردسترس نباشد و هیچ نوشتاری نمایش داده نشود. پیامک‌ها را برای فرایند بازیابی ضروری نکنید.

توجه: میانای پنهانی که پیام‌های گزارش را نمایش می‌دهد و به کاربر اجازه می‌دهد کنش‌ها را از منو انتخاب کند فقط به زبان انگلیسی دردسترس است.

نوار پیشرفت

نوارهای پیشرفت می‌توانند در زیر تصویر اصلی (یا پویانمایی) ظاهر شوند. نوار پیشرفت با ترکیب دو تصویر ورودی ساخته می‌شود که باید اندازه یکسانی داشته باشند:

نوار پیشرفت خالی

شکل ۹. progress_empty.png

نوار پیشرفت کامل

شکل ۱۰. progress_fill.png

انتهای سمت راست تصویر پر در کنار انتهای سمت چپ تصویر خالی نمایش داده می‌شود تا نوار پیشرفت ایجاد شود. موقعیت مرز بین دو تصویر تغییر می‌کند تا پیشرفت را نشان دهد. برای مثال، با جفت‌های تصویر ورودی بالا، نمایش:

نوار پیشرفت در ۱٪

شکل ۱۱. نوار پیشرفت روی ۱٪>

نوار پیشرفت روی ۱۰٪

شکل ۱۲. نوار پیشرفت در ۱۰٪

نوار پیشرفت در ۵۰٪

شکل ۱۳. نوار پیشرفت روی ۵۰٪

با قرار دادن نسخه‌های مختص دستگاه این تصاویر در (در این مثال) device/yoyodyne/tardis/recovery/res/images می‌توانید نسخه‌های مختص دستگاه این تصاویر را ارائه دهید. نام فایل‌ها باید با نام‌های فهرست‌شده در بالا مطابقت داشته باشد؛ وقتی فایلی در آن دایرکتوری پیدا شود، سیستم ساخت آن را به تصویر پیش‌فرض مربوطه ترجیح می‌دهد. فقط PNG در قالب RGB یا RGBA با عمق رنگ ۸ بیتی پشتیبانی می‌شود.

توجه: در Android 5.x، اگر مکان برای بازیابی شناخته‌شده باشد و زبان راست‌به‌چپ (RTL) (عربی، عبری، و غیره) باشد، نوار پیشرفت از راست به چپ پر می‌شود.

دستگاه‌های بدون صفحه

همه دستگاه‌های Android صفحه نمایش ندارند. اگر دستگاهتان لوازم بدون نمایشگر است یا میانای فقط صوتی دارد، ممکن است لازم باشد سفارشی‌سازی گسترده‌تری برای میانای کاربری بازیابی انجام دهید. به‌جای ایجاد زیرکلاس ScreenRecoveryUI، مستقیماً زیرکلاس کلاس والد آن یعنی RecoveryUI را ایجاد کنید.

‫RecoveryUI روش‌هایی برای مدیریت عملیات رابط کاربری سطح پایین‌تر مثل «روشن/خاموش کردن نمایشگر»، «به‌روزرسانی نوار پیشرفت»، «نمایش منو»، «تغییر انتخاب منو»، و غیره دارد. می‌توانید این موارد را ملغی کنید تا میانای مناسبی برای دستگاهتان ارائه دهید. شاید دستگاهتان چراغ‌های LED داشته باشد که می‌توانید از رنگ‌ها یا الگوهای چشمک‌زن مختلف برای نشان دادن وضعیت استفاده کنید، یا شاید بتوانید صدا پخش کنید. (شاید اصلاً نخواهید از منو یا حالت «نمایش نوشتار» پشتیبانی کنید؛ می‌توانید با پیاده‌سازی‌های CheckKey() و HandleMenuKey() که هرگز نمایشگر را روشن نمی‌کنند یا مورد منو را انتخاب نمی‌کنند، از دسترسی به آن‌ها جلوگیری کنید. در این حالت، بسیاری از روش‌های RecoveryUI که باید ارائه دهید می‌توانند فقط کدهای ناقص خالی باشند.)

برای دیدن روش‌هایی که باید پشتیبانی کنید، به bootable/recovery/ui.h برای اعلام RecoveryUI مراجعه کنید. 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;
    }

بررسی کردن «تهی» و آزاد کردن آرگومان‌های قبلاً ارزیابی‌شده می‌تواند برای آرگومان‌های متعدد خسته‌کننده باشد. تابع 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*—به‌طور خاص عضو داده—به آن اشاره می‌کند می‌پذیرد.

در این مورد، می‌خواهید مقدار درست یا غلط را برگردانید تا موفقیت را نشان دهید. به‌خاطر داشته باشید که براساس قرارداد، رشته خالی نادرست و همه رشته‌های دیگر درست هستند. باید یک شیء «مقدار» با یک کپی تخصیص‌یافته از رشته ثابت برای بازگرداندن تخصیص دهید، زیرا تماس‌گیرنده هر دو را 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 را ندارند)، می‌توانید آن‌ها را در 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

این‌ها به دلایل تاریخی فایل‌های رادیویی نامیده می‌شوند؛ ممکن است هیچ ارتباطی با رادیو دستگاه (درصورت وجود) نداشته باشند. این‌ها صرفاً لکه‌های مات داده‌ای هستند که سیستم ساخت در فایل .zip هدف که توسط ابزارهای تولید OTA استفاده می‌شود کپی می‌کند. وقتی ساخت انجام می‌دهید، 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"));""")

تابع جداگانه‌ای مورد تولید بسته OTA افزایشی را مدیریت می‌کند. برای این مثال، فرض کنید که می‌خواهید 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()
مشابه با همتای بسته کامل OTA، این دستور در انتهای تولید دستورگان فراخوانی می‌شود، پس‌از اینکه دستورات دستورگان برای به‌روزرسانی پارتیشن‌های بوت و سیستم صادر شده است. همچنین می‌توانید فرمان‌های اضافی برای به‌روزرسانی‌های مختص دستگاه صادر کنید.

توجه: اگر برق دستگاه قطع شود، ممکن است نصب OTA از ابتدا بازراه‌اندازی شود. آماده باشید تا با دستگاه‌هایی که این فرمان‌ها قبلاً به‌طور کامل یا جزئی روی آن‌ها اجرا شده است کنار بیایید.

انتقال توابع به اشیای اطلاعاتی

توابع را به یک شیء اطلاعاتی واحد که حاوی موارد مفید مختلف است منتقل کنید:

  • info.input_zip. (فقط OTA کامل) شیء zipfile.ZipFile برای فایل‌های هدف ورودی .zip.
  • info.source_zip. (فقط «به‌روزرسانی‌های ازطریق شبکه» افزایشی) شیء zipfile.ZipFile برای فایل‌های هدف منبع ‎ .zip (ساختمانی که قبلاً در دستگاه وجود دارد وقتی بسته افزایشی درحال نصب است).
  • info.target_zip. (فقط «به‌روزرسانی‌های ازطریق هوا» افزایشی) شیء zipfile.ZipFile برای فایل‌های هدف .zip هدف (ساختمانی که بسته افزایشی در دستگاه قرار می‌دهد).
  • info.output_zip. بسته درحال ایجاد است؛ شیء zipfile.ZipFile برای نوشتن باز شد. از common.ZipWriteStr(info.output_zip, filename, data) برای افزودن فایل به بسته استفاده کنید.
  • info.script. شیء نوشتاری که می‌توانید فرمان‌ها را به آن پیوست کنید. برای برونداد کردن نوشتار در دستورگان، با info.script.AppendExtra(script_text) تماس بگیرید. مطمئن شوید نوشتار برونداد با نقطه‌ویرگول تمام می‌شود تا با فرمان‌های صادرشده بعدی تداخل نداشته باشد.

برای جزئیات مربوط به شیء اطلاعات، به مستندات «بنیاد نرم‌افزار Python» برای بایگانی‌های ZIP مراجعه کنید.

مکان واحد را مشخص کنید

مکان اسکریپت releasetools.py دستگاهتان را در فایل BoardConfig.mk مشخص کنید:

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)، اگر فایل releasetools.py در فایل .zip هدف-فایل‌ها وجود داشته باشد، به فایل موجود در درخت منبع Android ترجیح داده می‌شود. همچنین می‌توانید با گزینه -s (یا --device_specific) مسیر افزونه‌های مختص دستگاه را به‌طور صریح مشخص کنید که بالاترین اولویت را دارد. این کار به شما امکان می‌دهد خطاها را اصلاح کنید و در افزونه‌های releasetools تغییر ایجاد کنید و این تغییرات را در فایل‌های هدف قدیمی اعمال کنید.

اکنون، وقتی ota_from_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 مراجعه کنید.

سازوکار نصب غیررسمی

«بازیابی» دارای سازوکار بارگیری جانبی برای نصب دستی بسته به‌روزرسانی بدون بارگیری آن ازطریق سیستم اصلی است. بارگیری جانبی برای اشکال‌زدایی یا ایجاد تغییرات در دستگاه‌هایی که سیستم اصلی آن‌ها راه‌اندازی نمی‌شود مفید است.

ازنظر تاریخی، بارگیری جانبی ازطریق بار کردن بسته‌ها از کارت SD دستگاه انجام شده است؛ در مورد دستگاهی که بوت نمی‌شود، بسته را می‌توان بااستفاده از رایانه دیگری روی کارت SD قرار داد و سپس کارت SD را در دستگاه قرار داد. برای سازگاری با دستگاه‌های Android بدون فضای ذخیره‌سازی خارجی قابل جابه‌جایی، بازیابی از دو سازوکار اضافی برای بارگیری جانبی پشتیبانی می‌کند: بار کردن بسته‌ها از پارتیشن حافظه نهان، و بار کردن آن‌ها ازطریق USB بااستفاده از adb.

برای فراخوانی هریک از سازوکارهای بارگیری جانبی، روش Device::InvokeMenuItem() دستگاه شما می‌تواند مقادیر زیر را برای BuiltinAction برگرداند:

  • APPLY_EXT. بسته به‌روزرسانی را از فضای ذخیره‌سازی خارجی ( /sdcard دایرکتوری) نصب غیررسمی کنید. ‫recovery.fstab شما باید نقطه نصب /sdcard را تعریف کند. این در دستگاه‌هایی که کارت SD را با پیوند نمادین به /data (یا سازوکار مشابهی) شبیه‌سازی می‌کنند قابل‌استفاده نیست. /data معمولاً برای بازیابی دردسترس نیست زیرا ممکن است رمزگذاری شده باشد. واسط کاربر بازیابی منویی از فایل‌های .zip در /sdcard نمایش می‌دهد و به کاربر امکان می‌دهد یکی را انتخاب کند.
  • APPLY_CACHE. مشابه بار کردن بسته از /sdcard، با این تفاوت که به‌جای آن از دایرکتوری /cache (که همیشه برای بازیابی دردسترس است) استفاده می‌شود. در سیستم معمولی، /cache فقط توسط کاربران دارای امتیاز قابل‌نوشتن است، و اگر دستگاه قابل‌راه‌اندازی نباشد، نمی‌توان در پوشه /cache چیزی نوشت (که این سازوکار را با کاربرد محدود می‌کند).
  • APPLY_ADB_SIDELOAD. به کاربر اجازه می‌دهد بسته‌ای را ازطریق کابل USB و ابزار توسعه adb به دستگاه ارسال کند. وقتی این سازوکار فراخوانده می‌شود، بازیابی نسخه کوچک خودش از شبح adbd را راه‌اندازی می‌کند تا adb در رایانه میزبان متصل بتواند با آن ارتباط برقرار کند. این نسخه کوچک فقط از یک فرمان پشتیبانی می‌کند: adb sideload filename. فایل نام‌گذاری‌شده از ماشین میزبان به دستگاه ارسال می‌شود، سپس دستگاه آن را درستی‌سنجی و نصب می‌کند، همان‌طور که اگر در فضای ذخیره‌سازی محلی بود.

چند نکته احتیاطی:

  • فقط از انتقال USB پشتیبانی می‌شود.
  • اگر بازیابی شما adbd را به‌طور عادی اجرا کند (معمولاً برای ساخت‌های userdebug و eng درست است)، این بازیابی در مد بارگذاری جانبی adb خاموش خواهد شد و وقتی بارگذاری جانبی adb دریافت بسته را تمام کرد، بازراه‌اندازی خواهد شد. وقتی در حالت بارگیری جانبی adb هستید، هیچ فرمان adb دیگری به‌جز sideload کار نمی‌کند ( logcat، reboot، push، pull، shell، و غیره همه ناموفق هستند).
  • نمی‌توانید از حالت بارگیری جانبی adb در دستگاه خارج شوید. برای لغو کردن، می‌توانید /dev/null (یا هر چیز دیگری که بسته معتبری نباشد) را به‌عنوان بسته ارسال کنید، و سپس دستگاه نمی‌تواند آن را درستی‌سنجی کند و فرایند نصب را متوقف می‌کند. پیاده‌سازی RecoveryUI متد CheckKey() همچنان برای فشار دادن کلیدها فراخوانده خواهد شد، بنابراین می‌توانید توالی کلیدی ارائه دهید که دستگاه را بازراه‌اندازی کند و در حالت بارگذاری جانبی adb کار کند.