سیستم بازیابی شامل چندین قلاب برای درج کد مخصوص دستگاه است تا بهروزرسانیهای OTA بتوانند بخشهایی از دستگاه غیر از سیستم اندروید (مثلاً باند پایه یا پردازنده رادیویی) را نیز بهروزرسانی کنند.
بخشها و مثالهای زیر، دستگاه tardis تولید شده توسط فروشنده yoyodyne را سفارشیسازی میکنند.
نقشه پارتیشن
از اندروید ۲.۳ به بعد، این پلتفرم از دستگاههای فلش eMMc و سیستم فایل ext4 که روی آن دستگاهها اجرا میشود، پشتیبانی میکند. همچنین از دستگاههای فلش Memory Technology Device (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 روی یک دستگاه فلش MTD. "device" باید نام پارتیشن MTD باشد و باید در
/proc/mtdظاهر شود. - ام تی دی
- یک پارتیشن خام MTD، که برای پارتیشنهای قابل بوت مانند بوت و ریکاوری استفاده میشود. MTD در واقع نصب نشده است، اما نقطه اتصال آن به عنوان کلیدی برای یافتن پارتیشن استفاده میشود. "device" باید نام پارتیشن MTD در
/proc/mtdباشد. - ex4
- یک سیستم فایل ext4 روی یک دستگاه فلش eMMc. "device" باید مسیر دستگاه بلوکی باشد.
- ایم ام سی
- یک دستگاه بلوک خام eMMc، که برای پارتیشنهای قابل بوت مانند بوت و ریکاوری استفاده میشود. مشابه نوع mtd، eMMc هرگز در واقع نصب نمیشود، اما رشته نقطه نصب برای یافتن دستگاه در جدول استفاده میشود.
- وی اف تی
- یک سیستم فایل FAT روی یک دستگاه بلوکی، معمولاً برای حافظههای خارجی مانند کارت SD. این دستگاه، دستگاه بلوکی است؛ دستگاه ۲ یک دستگاه بلوکی دوم است که سیستم در صورت عدم موفقیت در نصب دستگاه اصلی، سعی میکند آن را نصب کند (برای سازگاری با کارتهای SD که ممکن است با جدول پارتیشن فرمت شده باشند یا نباشند).
همه پارتیشنها باید در دایرکتوری ریشه نصب شوند (یعنی مقدار نقطه نصب باید با یک اسلش شروع شود و هیچ اسلش دیگری نداشته باشد). این محدودیت فقط برای نصب سیستم فایلها در حالت ریکاوری اعمال میشود؛ سیستم اصلی میتواند آنها را در هر جایی نصب کند. دایرکتوریهای
/boot،/recoveryو/miscباید از نوع خام (mtd یا emmc) باشند، در حالی که دایرکتوریهای/system،/data،/cacheو/sdcard(در صورت وجود) باید از نوع سیستم فایل (yaffs2، ext4 یا vfat) باشند.
از اندروید ۳.۰ به بعد، فایل recovery.fstab یک فیلد اختیاری اضافی به نام options اضافه میکند. در حال حاضر تنها گزینه تعریف شده length است که به شما امکان میدهد طول پارتیشن را به طور صریح مشخص کنید. این طول هنگام فرمت مجدد پارتیشن استفاده میشود (مثلاً برای پارتیشن userdata در حین عملیات پاک کردن دادهها/بازنشانی به تنظیمات کارخانه، یا برای پارتیشن سیستم در حین نصب یک بسته کامل OTA). اگر مقدار length منفی باشد، اندازه مورد نظر برای فرمت با اضافه کردن مقدار length به اندازه واقعی پارتیشن به دست میآید. به عنوان مثال، تنظیم "length=-16384" به این معنی است که ۱۶ کیلوبایت آخر آن پارتیشن هنگام فرمت مجدد آن پارتیشن رونویسی نخواهد شد. این ویژگی از ویژگیهایی مانند رمزگذاری پارتیشن userdata پشتیبانی میکند (جایی که فراداده رمزگذاری در انتهای پارتیشنی ذخیره میشود که نباید رونویسی شود).
نکته: فیلدهای device2 و options اختیاری هستند و باعث ایجاد ابهام در تجزیه میشوند. اگر ورودی در فیلد چهارم در خط با کاراکتر '/' شروع شود، به عنوان ورودی device2 در نظر گرفته میشود؛ اگر ورودی با کاراکتر '/' شروع نشود، به عنوان فیلد options در نظر گرفته میشود.
انیمیشن بوت
تولیدکنندگان دستگاهها میتوانند انیمیشن نمایش داده شده هنگام بوت شدن دستگاه اندروید را سفارشی کنند. برای انجام این کار، یک فایل .zip بسازید که طبق مشخصات در قالب bootanimation سازماندهی و قرار داده شده باشد.
برای دستگاههای Android Things ، میتوانید فایل زیپشده را در کنسول Android Things آپلود کنید تا تصاویر در محصول انتخابشده قرار گیرند.
توجه: این تصاویر باید با دستورالعملهای برند اندروید مطابقت داشته باشند. برای دستورالعملهای برند، به بخش اندروید مرکز بازاریابی شرکا مراجعه کنید.
رابط کاربری بازیابی
برای پشتیبانی از دستگاههایی با سختافزارهای مختلف (دکمههای فیزیکی، 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"
توابع سربرگ و آیتم
کلاس Device به توابعی برای برگرداندن هدرها و آیتمهایی که در منوی مخفی بازیابی ظاهر میشوند، نیاز دارد. هدرها نحوهی کار با منو (یعنی کنترلهایی برای تغییر/انتخاب آیتم هایلایت شده) را توصیف میکنند.
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 دارای صفحه نمایش است، بنابراین میتوانید از پیادهسازی داخلی 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() صرف نظر از اینکه در ادامهی فرآیند بازیابی چه اتفاقی میافتد، فراخوانی میشود: وقتی منو خاموش میشود، وقتی روشن است، در حین نصب بسته، در حین پاک کردن دادههای کاربر و غیره. این تابع میتواند یکی از چهار ثابت زیر را برگرداند:
- فعال یا غیرفعال کردن نمایش منو و/یا ورود متنی
- ریبوت . فوراً دستگاه را ریبوت کنید.
- نادیده گرفتن . این فشردن کلید را نادیده بگیر
- ENQUEUE . این کلید فشرده شده را برای مصرف همزمان (یعنی توسط سیستم منوی بازیابی در صورت فعال بودن نمایشگر) در نوبت قرار میدهد.
CheckKey() هر بار که یک رویداد key-down با یک رویداد key-up برای همان کلید دنبال میشود، فراخوانی میشود. (دنباله رویدادهای A-down B-down B-up A-up فقط منجر به فراخوانی 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، میتوانید متغیر animation_fps برای کنترل سرعت انیمیشنها بر حسب فریم در ثانیه (FPS) تنظیم کنید.
نکته: اسکریپت فعلی interlace-frames.py به شما امکان میدهد اطلاعات animation_fps را در خود تصویر ذخیره کنید. در نسخههای قبلی اندروید، لازم بود animation_fps خودتان تنظیم کنید.
برای تنظیم متغیر animation_fps ، تابع ScreenRecoveryUI::Init() را در زیرکلاس خود بازنویسی کنید. مقدار را تنظیم کنید، سپس تابع parent Init() را برای تکمیل مقداردهی اولیه فراخوانی کنید. مقدار پیشفرض (20 FPS) مربوط به تصاویر بازیابی پیشفرض است؛ هنگام استفاده از این تصاویر، نیازی به ارائه تابع Init() ندارید. برای جزئیات بیشتر در مورد تصاویر، به Recovery UI Images مراجعه کنید.
کلاس دستگاه
پس از پیادهسازی RecoveryUI، کلاس دستگاه خود را تعریف کنید (که از کلاس داخلی Device مشتق شده است). این کلاس باید یک نمونه واحد از کلاس UI شما ایجاد کند و آن را از تابع GetUI() برگرداند:
class TardisDevice : public Device { private: TardisUI* ui; public: TardisDevice() : ui(new TardisUI) { } RecoveryUI* GetUI() { return ui; }
بازیابی را شروع کنید
متد 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() از شیء UI پردازش و در صف قرار گرفته است) و وضعیت فعلی قابلیت مشاهدهی منو/گزارش متن را دریافت میکند. مقدار بازگشتی یک عدد صحیح است. اگر مقدار 0 یا بالاتر باشد، به عنوان موقعیت یک آیتم منو در نظر گرفته میشود که بلافاصله فراخوانی میشود (به متد InvokeMenuItem() در زیر مراجعه کنید). در غیر این صورت میتواند یکی از ثابتهای از پیش تعریف شدهی زیر باشد:
- kHighlightUp . هایلایت منو را به مورد قبلی منتقل میکند
- kHighlightDown . هایلایت منو را به مورد بعدی منتقل میکند
- kInvokeItem . آیتم هایلایت شدهی فعلی را فراخوانی میکند.
- kNoAction . با فشردن این کلید هیچ کاری انجام ندهید
همانطور که از آرگومان visible پیداست، HandleMenuKey() حتی اگر منو قابل مشاهده نباشد نیز فراخوانی میشود. برخلاف CheckKey() ، این تابع در حالی که recovery در حال انجام کاری مانند پاک کردن دادهها یا نصب یک بسته است، فراخوانی نمیشود - فقط زمانی فراخوانی میشود که recovery در حالت غیرفعال و منتظر ورودی باشد.
مکانیزمهای گوی گوی
اگر دستگاه شما مکانیزم ورودی شبیه به ترکبال دارد (رویدادهای ورودی با نوع EV_REL و کد REL_Y تولید میکند)، ریکاوری هر زمان که دستگاه ورودی شبیه به ترکبال حرکت در محور Y را گزارش دهد، کلیدهای فشرده شده KEY_UP و KEY_DOWN را ترکیب میکند. تنها کاری که باید انجام دهید این است که رویدادهای KEY_UP و KEY_DOWN را به اقدامات منو نگاشت کنید. این نگاشت برای CheckKey() انجام نمیشود ، بنابراین نمیتوانید از حرکات ترکبال به عنوان محرک برای راهاندازی مجدد یا تغییر وضعیت صفحه نمایش استفاده کنید.
کلیدهای اصلاحکننده
برای بررسی اینکه آیا کلیدهای فشرده شده به عنوان اصلاحکننده نگه داشته شدهاند یا خیر، متد IsKeyPressed() را از شیء رابط کاربری خود فراخوانی کنید. برای مثال، در برخی دستگاهها، فشردن Alt-W در حالت ریکاوری، صرف نظر از اینکه منو قابل مشاهده باشد یا خیر، باعث پاک شدن دادهها میشود. میتوانید این کار را به صورت زیر پیادهسازی کنید:
int HandleMenuKey(int key, int visible) { if (ui->IsKeyPressed(KEY_LEFTALT) && key == KEY_W) { return 2; // position of the "wipe data" item in the menu } ... }
نکته: اگر visible برابر با false باشد، برگرداندن مقادیر ویژهای که منو را دستکاری میکنند (جابجایی هایلایت، فراخوانی آیتم هایلایت شده) منطقی نیست، زیرا کاربر نمیتواند هایلایت را ببیند. با این حال، در صورت تمایل میتوانید مقادیر را برگردانید.
آیتم منو را فراخوانی کنید
در مرحله بعد، یک متد InvokeMenuItem() ارائه دهید که موقعیتهای صحیح را در آرایهای از آیتمهای برگردانده شده توسط GetMenuItems() به اکشنها نگاشت کند. برای آرایهای از آیتمها در مثال tardis، از دستور زیر استفاده کنید:
BuiltinAction InvokeMenuItem(int menu_position) { switch (menu_position) { case 0: return REBOOT; case 1: return APPLY_ADB_SIDELOAD; case 2: return WIPE_DATA; case 3: return WIPE_CACHE; default: return NO_ACTION; } }
این متد میتواند هر عضوی از شمارش BuiltinAction را برگرداند تا به سیستم بگوید آن عمل را انجام دهد (یا اگر میخواهید سیستم هیچ کاری انجام ندهد، عضو NO_ACTION را برگرداند). این جایی است که میتوانید قابلیتهای بازیابی اضافی فراتر از آنچه در سیستم وجود دارد را ارائه دهید: یک آیتم برای آن در منوی خود اضافه کنید، وقتی آن آیتم منو فراخوانی شد، آن را اینجا اجرا کنید و NO_ACTION را برگردانید تا سیستم کار دیگری انجام ندهد.
BuiltinAction شامل مقادیر زیر است:
- NO_ACTION . هیچ کاری نکن.
- ریبوت . از ریکاوری خارج شوید و دستگاه را به طور عادی ریبوت کنید.
- APPLY_EXT، APPLY_CACHE، APPLY_ADB_SIDELOAD . یک بسته بهروزرسانی را از مکانهای مختلف نصب کنید. برای جزئیات بیشتر، به Sideloading مراجعه کنید.
- WIPE_CACHE . فقط پارتیشن کش را فرمت کنید. نیازی به تأیید نیست زیرا این روش نسبتاً بیضرر است.
- WIPE_DATA . پارتیشنهای userdata و cache را فرمت مجدد کنید، که به عنوان بازنشانی به تنظیمات کارخانه نیز شناخته میشود. قبل از ادامه، از کاربر خواسته میشود که این اقدام را تأیید کند.
آخرین متد، WipeData() ، اختیاری است و هر زمان که عملیات پاک کردن دادهها آغاز شود (چه از طریق بازیابی از طریق منو و چه زمانی که کاربر تصمیم به تنظیم مجدد کارخانه از سیستم اصلی گرفته باشد) فراخوانی میشود. این متد قبل از پاک شدن پارتیشنهای دادههای کاربر و حافظه پنهان فراخوانی میشود. اگر دستگاه شما دادههای کاربر را در جایی غیر از آن دو پارتیشن ذخیره میکند، باید آن را در اینجا پاک کنید. برای نشان دادن موفقیت باید 0 و برای شکست مقدار دیگری را برگردانید، اگرچه در حال حاضر مقدار بازگشتی نادیده گرفته میشود. پارتیشنهای دادههای کاربر و حافظه پنهان چه با موفقیت و چه با شکست، پاک میشوند.
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
تصاویر رابط کاربری بازیابی
رابط کاربری ریکاوری شامل تصاویر است. در حالت ایدهآل، کاربران هرگز با رابط کاربری تعامل ندارند: در طول یک بهروزرسانی عادی، تلفن بدون هیچ ورودی از کاربر، وارد ریکاوری میشود، نوار پیشرفت نصب را پر میکند و دوباره به سیستم جدید بوت میشود. در صورت بروز مشکل در بهروزرسانی سیستم، تنها اقدامی که کاربر میتواند انجام دهد تماس با پشتیبانی مشتری است.
یک رابط کاربری صرفاً تصویری، نیاز به بومیسازی را از بین میبرد. با این حال، از اندروید ۵.۰ به بعد، بهروزرسانی میتواند رشتهای از متن (مثلاً "در حال نصب بهروزرسانی سیستم...") را همراه با تصویر نمایش دهد. برای جزئیات بیشتر، به متن بازیابی بومیسازی شده مراجعه کنید.
اندروید ۵.۰ و بالاتر
رابط کاربری ریکاوری اندروید ۵.۰ و نسخههای جدیدتر از دو تصویر اصلی استفاده میکند: تصویر خطا و انیمیشن نصب .
شکل ۱. icon_error.png | شکل ۲. 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 را وارد کنید و تعداد فریمهای انیمیشن را روی ۰ تنظیم کنید (آیکون خطا متحرک نیست؛ همیشه یک تصویر ثابت است).
اندروید ۴.x و نسخههای قدیمیتر
رابط کاربری ریکاوری اندروید ۴.x و نسخههای قدیمیتر از تصویر خطا (نشان داده شده در بالا) و انیمیشن نصب به همراه چندین تصویر روی هم قرار گرفته استفاده میکند:
شکل ۳. 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 را وارد کنید و تعداد فریمهای انیمیشن را روی ۰ تنظیم کنید (آیکون خطا متحرک نیست؛ همیشه یک تصویر ثابت است).
متن بازیابی محلی
اندروید ۵.x رشتهای از متن (مثلاً "در حال نصب بهروزرسانی سیستم...") را به همراه تصویر نمایش میدهد. وقتی سیستم اصلی به حالت ریکاوری بوت میشود، زبان محلی فعلی کاربر را به عنوان یک گزینه خط فرمان به ریکاوری ارسال میکند. برای نمایش هر پیام، ریکاوری شامل یک تصویر ترکیبی دوم با رشتههای متنی از پیش رندر شده برای آن پیام در هر زبان است.
تصویر نمونه از رشتههای متن بازیابی:

شکل ۸. متن محلیشده برای پیامهای بازیابی
متن بازیابی میتواند پیامهای زیر را نمایش دهد:
- نصب بهروزرسانی سیستم...
- خطا!
- پاک کردن... (هنگام انجام پاک کردن دادهها/بازنشانی به تنظیمات کارخانه)
- بدون دستور (وقتی کاربر به صورت دستی وارد ریکاوری میشود)
برنامه اندروید در 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 با عمق رنگ ۸ بیتی پشتیبانی میشوند.
نکته: در اندروید ۵.x، اگر زبان محلی برای بازیابی شناخته شده باشد و یک زبان راست به چپ (RTL) (عربی، عبری و غیره) باشد، نوار پیشرفت از راست به چپ پر میشود.
دستگاههای بدون صفحه نمایش
همه دستگاههای اندروید صفحه نمایش ندارند. اگر دستگاه شما یک دستگاه بدون سر (headless appliance) است یا رابط کاربری فقط صوتی دارد، ممکن است نیاز به سفارشیسازی گستردهتری از رابط کاربری بازیابی داشته باشید. به جای ایجاد یک زیرکلاس از ScreenRecoveryUI، مستقیماً از کلاس والد آن یعنی RecoveryUI زیرکلاس بسازید.
RecoveryUI متدهایی برای مدیریت عملیات رابط کاربری سطح پایینتر مانند «تغییر وضعیت نمایش»، «بهروزرسانی نوار پیشرفت»، «نمایش منو»، «تغییر انتخاب منو» و غیره دارد. شما میتوانید این متدها را برای ارائه یک رابط کاربری مناسب برای دستگاه خود لغو کنید. شاید دستگاه شما دارای LEDهایی باشد که میتوانید از رنگها یا الگوهای مختلف چشمکزن برای نشان دادن وضعیت استفاده کنید، یا شاید بتوانید صدا پخش کنید. (شاید اصلاً نخواهید از منو یا حالت «نمایش متن» پشتیبانی کنید؛ میتوانید با پیادهسازیهای CheckKey() و HandleMenuKey() که هرگز صفحه نمایش را روشن یا یک آیتم منو را انتخاب نمیکنند، از دسترسی به آنها جلوگیری کنید. در این حالت، بسیاری از متدهای RecoveryUI که باید ارائه دهید، میتوانند صرفاً stubهای خالی باشند.)
برای اطلاع از متدهایی که باید پشتیبانی کنید، به bootable/recovery/ui.h مراجعه کنید تا بتوانید اعلان RecoveryUI را مشاهده کنید. RecoveryUI انتزاعی است - برخی از متدها مجازی خالص هستند و باید توسط زیرکلاسها ارائه شوند - اما شامل کدی برای پردازش ورودیهای کلید است. اگر دستگاه شما کلید ندارد یا میخواهید آنها را به طور متفاوتی پردازش کنید، میتوانید آن را نیز بازنویسی کنید.
بهروزرسانیکننده
شما میتوانید با ارائه توابع افزونه خودتان که میتوانند از درون اسکریپت بهروزرسانی شما فراخوانی شوند، از کد مخصوص دستگاه در نصب بسته بهروزرسانی استفاده کنید. در اینجا یک تابع نمونه برای دستگاه tardis آورده شده است:
device/yoyodyne/tardis/recovery/recovery_updater.c
#include <stdlib.h> #include <string.h> #include "edify/expr.h"
هر تابع افزونه امضای یکسانی دارد. آرگومانها عبارتند از نامی که تابع با آن فراخوانی شده است، یک کوکی State* ، تعداد آرگومانهای ورودی و آرایهای از اشارهگرهای Expr* که نشاندهنده آرگومانها هستند. مقدار بازگشتی یک Value* است که به تازگی اختصاص داده شده است.
Value* ReprogramTardisFn(const char* name, State* state, int argc, Expr* argv[]) { if (argc != 2) { return ErrorAbort(state, "%s() expects 2 args, got %d", name, argc); }
آرگومانهای شما در زمان فراخوانی تابع ارزیابی نشدهاند - منطق تابع شما تعیین میکند که کدام یک از آنها و چند بار ارزیابی شوند. بنابراین، میتوانید از توابع افزونه برای پیادهسازی ساختارهای کنترلی خود استفاده کنید. برای ارزیابی یک آرگومان Expr* و بازگرداندن یک Value* ، Call Evaluate() . اگر 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* به آن اشاره میشود، به ویژه عضو داده، در اختیار میگیرد.
در این مثال، شما میخواهید یک مقدار درست یا نادرست را برای نشان دادن موفقیت برگردانید. به یاد داشته باشید که قرارداد این است که رشته خالی نادرست و تمام رشتههای دیگر درست هستند. شما باید یک شیء Value را با یک کپی 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);
}اکنون میتوانید makefile را برای ساخت یک کتابخانه استاتیک با کد خود پیکربندی کنید. (این همان 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 فهرست کنید تا بدون فراخوانی تابع ثبت (غیرموجود) آنها، به updater پیوند دهید. به عنوان مثال، اگر کد مخصوص دستگاه شما میخواست از 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 خود، اسکریپت بهروزرسانی ممکن است شامل موارد زیر باشد: tardis.reprogram("the-key", package_extract_file("tardis-image.dat")) . این از نسخه تک آرگومانی تابع داخلی package_extract_file() استفاده میکند که محتویات یک فایل استخراج شده از بسته بهروزرسانی را به عنوان یک blob برای تولید آرگومان دوم به تابع افزونه جدید برمیگرداند.
تولید بسته 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
به دلایل تاریخی، به این فایلها، فایلهای رادیویی گفته میشود؛ ممکن است هیچ ارتباطی با رادیوی دستگاه (در صورت وجود) نداشته باشند. آنها صرفاً حبابهای مبهمی از دادهها هستند که سیستم ساخت، آنها را در فایل target-files.zip که توسط ابزارهای تولید OTA استفاده میشود، کپی میکند. وقتی شما یک ساخت انجام میدهید، tardis.dat در فایل target-files.zip به عنوان RADIO/tardis.dat ذخیره میشود. میتوانید چندین بار تابع add-radio-file فراخوانی کنید تا هر تعداد فایلی که میخواهید اضافه کنید.
ماژول پایتون
برای گسترش ابزارهای انتشار، یک ماژول پایتون (باید با نام 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() - نزدیک به شروع تولید یک OTA کامل فراخوانی میشود. این مکان خوبی برای انتشار اظهاراتی در مورد وضعیت فعلی دستگاه است. دستورات اسکریپتی را که باعث ایجاد تغییر در دستگاه میشوند، منتشر نکنید.
-
FullOTA_InstallBegin() - پس از ارسال تمام assertionهای مربوط به وضعیت دستگاه اما قبل از ایجاد هرگونه تغییری، فراخوانی میشود. میتوانید دستوراتی را برای بهروزرسانیهای مخصوص دستگاه منتشر کنید که باید قبل از تغییر هر چیز دیگری در دستگاه اجرا شوند.
-
FullOTA_InstallEnd() - در پایان تولید اسکریپت، پس از انتشار دستورات اسکریپت برای بهروزرسانی پارتیشنهای بوت و سیستم، فراخوانی میشود. همچنین میتوانید دستورات اضافی را برای بهروزرسانیهای خاص دستگاه منتشر کنید.
-
IncrementalOTA_Assertions() - مشابه
FullOTA_Assertions()است اما هنگام تولید یک بسته بهروزرسانی افزایشی فراخوانی میشود. -
IncrementalOTA_VerifyBegin() - پس از اتمام تمام assertionهای مربوط به وضعیت دستگاه اما قبل از ایجاد هرگونه تغییری، فراخوانی میشود. میتوانید دستوراتی را برای بهروزرسانیهای مخصوص دستگاه منتشر کنید که باید قبل از تغییر هر چیز دیگری در دستگاه اجرا شوند.
-
IncrementalOTA_VerifyEnd() - در پایان مرحله تأیید، زمانی که اسکریپت تأیید کرد فایلهایی که قرار است لمس کند، محتوای اولیه مورد انتظار را دارند، فراخوانی میشود. در این مرحله هیچ چیز در دستگاه تغییر نکرده است. همچنین میتوانید کدی را برای تأییدهای اضافی مخصوص دستگاه منتشر کنید.
-
IncrementalOTA_InstallBegin() - پس از اینکه فایلهایی که باید وصله شوند، تأیید شدند که دارای وضعیت قبل از مورد انتظار هستند، اما قبل از ایجاد هرگونه تغییر، فراخوانی میشوند. میتوانید دستوراتی را برای بهروزرسانیهای خاص دستگاه منتشر کنید که باید قبل از تغییر هر چیز دیگری در دستگاه اجرا شوند.
-
IncrementalOTA_InstallEnd() - مشابه بسته کامل OTA، این بسته در پایان تولید اسکریپت، پس از انتشار دستورات اسکریپت برای بهروزرسانی بوت و پارتیشنهای سیستم، فراخوانی میشود. همچنین میتوانید دستورات اضافی را برای بهروزرسانیهای خاص دستگاه منتشر کنید.
توجه: اگر دستگاه خاموش شود، نصب OTA ممکن است از ابتدا مجدداً راهاندازی شود. برای مقابله با دستگاههایی که این دستورات قبلاً به طور کامل یا جزئی روی آنها اجرا شدهاند، آماده باشید.
توابع را به اشیاء اطلاعاتی منتقل کنید
توابع را به یک شیء اطلاعات واحد که شامل موارد مفید مختلفی است، ارسال کنید:
- info.input_zip . (فقط فایلهای OTA کامل) شیء
zipfile.ZipFileبرای فایل ورودی target-files.zip. - info.source_zip . (فقط OTA های افزایشی) شیء
zipfile.ZipFileبرای فایل منبع target-files. (فایلی که هنگام نصب بسته افزایشی از قبل روی دستگاه نصب شده است). - info.target_zip (فقط OTA های افزایشی) شیء
zipfile.ZipFileبرای فایل target-files.zip (فایلی که بسته افزایشی روی دستگاه قرار میدهد). - info.output_zip . بسته در حال ایجاد است؛ یک شیء
zipfile.ZipFileبرای نوشتن باز شده است. برای افزودن یک فایل به بسته از common.ZipWriteStr(info.output_zip, filename , data ) استفاده کنید. - info.script . شیء اسکریپت که میتوانید دستورات را به آن اضافه کنید. برای خروجی متن در اسکریپت، تابع
info.script.AppendExtra( script_text )را فراخوانی کنید. مطمئن شوید که متن خروجی با یک نقطه ویرگول (;) تمام میشود تا در دستوراتی که پس از آن منتشر میشوند، اجرا نشود.
برای جزئیات بیشتر در مورد شیء info، به مستندات بنیاد نرمافزار پایتون برای بایگانیهای 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 در فایل target-files.zip ( META/releasetools.py ) گنجانده شده است.
وقتی ابزارهای انتشار (چه img_from_target_files و چه ota_from_target_files ) را اجرا میکنید، اسکریپت releasetools.py در فایل target-files.zip، در صورت وجود، نسبت به اسکریپت موجود در درخت منبع اندروید ارجحیت دارد. همچنین میتوانید مسیر افزونههای مخصوص دستگاه را با گزینه -s (یا --device_specific ) که اولویت اصلی را دارد، به طور صریح مشخص کنید. این به شما امکان میدهد خطاها را اصلاح کنید و در افزونههای releasetools تغییراتی ایجاد کنید و آن تغییرات را در فایلهای هدف قدیمی اعمال کنید.
حالا، وقتی ota_from_target_files اجرا میکنید، به طور خودکار ماژول مخصوص دستگاه را از فایل target_files.zip دریافت کرده و هنگام تولید بستههای OTA از آن استفاده میکند:
./build/make/tools/releasetools/ota_from_target_files \
-i PREVIOUS-tardis-target_files.zip \
dist_output/tardis-target_files.zip \
incremental_ota_update.zip
از طرف دیگر، میتوانید هنگام اجرای ota_from_target_files افزونههای مخصوص دستگاه را مشخص کنید.
./build/make/tools/releasetools/ota_from_target_files \
-s device/yoyodyne/tardis \
-i PREVIOUS-tardis-target_files.zip \
dist_output/tardis-target_files.zip \
incremental_ota_update.zip
نکته: برای مشاهده لیست کامل گزینهها، به توضیحات ota_from_target_files در build/make/tools/releasetools/ota_from_target_files مراجعه کنید.
مکانیسم بارگذاری جانبی
ریکاوری دارای مکانیزم سایدلودینگ برای نصب دستی یک بسته بهروزرسانی بدون دانلود آن به صورت بیسیم توسط سیستم اصلی است. سایدلودینگ برای اشکالزدایی یا ایجاد تغییرات در دستگاههایی که سیستم اصلی قابل بوت شدن نیست، مفید است.
از لحاظ تاریخی، سایدلودینگ از طریق بارگیری بستهها از کارت SD دستگاه انجام میشد؛ در مورد دستگاهی که بوت نمیشود، میتوان بسته را با استفاده از یک کامپیوتر دیگر روی کارت SD قرار داد و سپس کارت SD را وارد دستگاه کرد. برای سازگاری با دستگاههای اندرویدی بدون حافظه خارجی قابل جابجایی، ریکاوری از دو مکانیسم اضافی برای سایدلودینگ پشتیبانی میکند: بارگیری بستهها از پارتیشن حافظه پنهان و بارگیری آنها از طریق USB با استفاده از adb.
برای فراخوانی هر مکانیزم سایدلود، Device::InvokeMenuItem() دستگاه شما میتواند مقادیر BuiltinAction زیر را برگرداند:
- APPLY_EXT . Sideload an update package from external storage (
/sdcarddirectory). Your recovery.fstab must define the/sdcardmount point. This is not usable on devices that emulate an SD card with a symlink to/data(or some similar mechanism)./datais typically not available to recovery because it may be encrypted. The recovery UI displays a menu of .zip files in/sdcardand allows the user to select one. - APPLY_CACHE . Similar to loading a package from
/sdcardexcept that the/cachedirectory (which is always available to recovery) is used instead. From the regular system,/cacheis only writable by privileged users, and if the device isn't bootable then the/cachedirectory can't be written to at all (which makes this mechanism of limited utility). - APPLY_ADB_SIDELOAD . Allows user to send a package to the device via a USB cable and the adb development tool. When this mechanism is invoked, recovery starts up its own mini version of the adbd daemon to let adb on a connected host computer talk to it. This mini version supports only a single command:
adb sideload filename. The named file is sent from the host machine to the device, which then verifies and installs it just as if it had been on local storage.
A few caveats:
- Only USB transport is supported.
- If your recovery runs adbd normally (usually true for userdebug and eng builds), that will be shut down while the device is in adb sideload mode and will be restarted when adb sideload has finished receiving a package. While in adb sideload mode, no adb commands other than
sideloadwork (logcat,reboot,push,pull,shell, etc. all fail). - You cannot exit adb sideload mode on the device. To abort, you can send
/dev/null(or anything else that's not a valid package) as the package, and then the device will fail to verify it and stop the installation procedure. The RecoveryUI implementation'sCheckKey()method will continue to be called for keypresses, so you can provide a key sequence that reboots the device and works in adb sideload mode.

