يشتمل نظام الاسترداد على عدة خطافات لإدخال رمز خاص بالجهاز بحيث يمكن لتحديثات OTA أيضًا تحديث أجزاء من الجهاز بخلاف نظام Android (على سبيل المثال ، النطاق الأساسي أو معالج الراديو).
الأقسام والأمثلة التالية تخصص جهاز التارديس الذي أنتجه بائع yoyodyne .
خريطة التقسيم
اعتبارًا من Android 2.3 ، يدعم النظام الأساسي أجهزة فلاش 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
، وهو اختياري ، يجب تحديد جميع نقاط التحميل في هذا المثال (قد تضيف الأجهزة أيضًا أقسامًا إضافية). هناك خمسة أنواع من أنظمة الملفات المدعومة:
- يافس 2
- نظام ملفات yaffs2 أعلى جهاز فلاش MTD. يجب أن يكون "الجهاز" هو اسم قسم MTD ويجب أن يظهر في
/proc/mtd
. - متد
- قسم MTD خام يستخدم للأقسام القابلة للتمهيد مثل التمهيد والاسترداد. لم يتم تثبيت MTD بالفعل ، ولكن يتم استخدام نقطة التحميل كمفتاح لتحديد موقع القسم. يجب أن يكون "الجهاز" هو اسم قسم MTD في
/proc/mtd
. - ext4
- نظام ملفات ext4 فوق جهاز فلاش eMMc. يجب أن يكون "الجهاز" هو مسار جهاز الحظر.
- emmc
- جهاز كتلة eMMc خام ، يستخدم للأقسام القابلة للتمهيد مثل التمهيد والاسترداد. على غرار النوع mtd ، لا يتم تثبيت eMMc فعليًا مطلقًا ، ولكن يتم استخدام سلسلة نقطة التحميل لتحديد موقع الجهاز في الجدول.
- vfat
- نظام ملفات FAT فوق جهاز كتلة ، عادةً للتخزين الخارجي مثل بطاقة SD. الجهاز هو جهاز الكتلة ؛ الجهاز 2 هو جهاز كتلة ثانٍ يحاول النظام تحميله في حالة فشل تثبيت الجهاز الأساسي (للتوافق مع بطاقات SD التي قد يتم تنسيقها أو لا يتم تنسيقها باستخدام جدول أقسام).
يجب تثبيت جميع الأقسام في الدليل الجذر (أي يجب أن تبدأ قيمة نقطة التحميل بشرطة مائلة ولا تحتوي على شرطات مائلة أخرى). ينطبق هذا القيد فقط على أنظمة الملفات المتصاعدة في عملية الاسترداد ؛ النظام الرئيسي مجاني لتركيبها في أي مكان. يجب أن تكون الدلائل
/boot
و/recovery
و/misc
من الأنواع الأولية (mtd أو emmc) ، بينما يجب أن تكون الدلائل/system
و/data
و/cache
و/sdcard
(إن وجدت) من أنواع أنظمة الملفات (yaffs2 أو ext4 أو vfat).
بدءًا من Android 3.0 ، يكتسب ملف recovery.fstab مجالًا اختياريًا وخيارات إضافية. الخيار الوحيد المحدد حاليًا هو الطول ، والذي يتيح لك تحديد طول القسم بشكل صريح. يُستخدم هذا الطول عند إعادة تهيئة القسم (على سبيل المثال ، لقسم بيانات المستخدم أثناء عملية مسح البيانات / إعادة ضبط المصنع ، أو لقسم النظام أثناء تثبيت حزمة OTA كاملة). إذا كانت قيمة الطول سالبة ، فسيتم أخذ الحجم المطلوب تنسيقه عن طريق إضافة قيمة الطول إلى حجم القسم الحقيقي. على سبيل المثال ، إعداد "length = -16384" يعني أنه لن يتم الكتابة فوق آخر 16 كيلو بايت من هذا القسم عند إعادة تنسيق هذا القسم. يدعم هذا ميزات مثل تشفير قسم بيانات المستخدم (حيث يتم تخزين بيانات تعريف التشفير في نهاية القسم الذي يجب عدم الكتابة فوقه).
ملاحظة: حقلي device2 وخيارات اختيارية ، مما يؤدي إلى الغموض في التحليل. إذا كان الإدخال في الحقل الرابع على السطر يبدأ بحرف "/" ، فإنه يعتبر إدخال device2 ؛ إذا لم يبدأ الإدخال بحرف "/" ، فإنه يعتبر حقل خيارات .
تمهيد الرسوم المتحركة
الشركات المصنعة للأجهزة لديها القدرة على تخصيص الرسوم المتحركة التي تظهر عند بدء تشغيل جهاز Android. للقيام بذلك ، أنشئ ملفًا بتنسيق zip. منظمًا وموجودًا وفقًا للمواصفات بتنسيق bootanimation .
بالنسبة لأجهزة Android Things ، يمكنك تحميل الملف المضغوط في وحدة تحكم Android Things لتضمين الصور في المنتج المحدد.
ملاحظة: يجب أن تتوافق هذه الصور مع إرشادات علامة 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 الخاص بجهازك. يفترض هذا المثال أن جهاز التارديس يحتوي على شاشة ، لذا يمكنك أن ترث من تنفيذ 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 حدث مفتاح المتابعة لنفس المفتاح. (يؤدي تسلسل الأحداث A-down B-down B-up A-up إلى CheckKey(B)
فقط.) يمكن CheckKey()
استدعاء IsKeyPressed()
، لمعرفة ما إذا كانت المفاتيح الأخرى مضغوطة أم لا. (في التسلسل أعلاه للأحداث الرئيسية ، إذا كان CheckKey(B)
يسمى IsKeyPressed(A)
لكان قد عاد صحيحًا.)
يمكن CheckKey()
الحفاظ على الحالة في فئتها ؛ يمكن أن يكون هذا مفيدًا لاكتشاف تسلسل المفاتيح. يوضح هذا المثال إعدادًا أكثر تعقيدًا بعض الشيء: يتم تبديل الشاشة بالضغط باستمرار على الطاقة والضغط على رفع مستوى الصوت ، ويمكن إعادة تشغيل الجهاز فورًا عن طريق الضغط على زر الطاقة خمس مرات متتالية (بدون مفاتيح متداخلة أخرى):
class TardisUI : public ScreenRecoveryUI { private: int consecutive_power_keys; public: TardisUI() : consecutive_power_keys(0) {} virtual KeyAction CheckKey(int key) { if (IsKeyPressed(KEY_POWER) && key == KEY_VOLUMEUP) { return TOGGLE; } if (key == KEY_POWER) { ++consecutive_power_keys; if (consecutive_power_keys >= 5) { return REBOOT; } } else { consecutive_power_keys = 0; } return ENQUEUE; } };
ScreenRecoveryUI
عند استخدام الصور الخاصة بك (رمز الخطأ ، الرسوم المتحركة للتثبيت ، أشرطة التقدم) مع ScreenRecoveryUI ، يمكنك تعيين animation_fps
المتغيرة في الثانية للتحكم في السرعة في الإطارات في الثانية (FPS) للرسوم المتحركة.
ملاحظة: يتيح لك البرنامج النصي الحالي interlace-frames.py
تخزين معلومات animation_fps
في الصورة نفسها. في الإصدارات السابقة من Android ، كان من الضروري تعيين animation_fps
بنفسك.
لتعيين animation_fps
المتغير ، قم بتجاوز وظيفة ScreenRecoveryUI::Init()
في الفئة الفرعية الخاصة بك. قم بتعيين القيمة ، ثم اتصل بوظيفة parent Init()
لإكمال التهيئة. تتوافق القيمة الافتراضية (20 إطارًا في الثانية) مع صور الاسترداد الافتراضية ؛ عند استخدام هذه الصور ، لن تحتاج إلى توفير وظيفة Init()
. للحصول على تفاصيل حول الصور ، راجع Recovery UI Images .
فئة الجهاز
بعد أن يكون لديك تطبيق RecoveryUI ، حدد فئة جهازك (فئة فرعية من فئة الجهاز المضمنة). يجب أن يُنشئ مثيلًا واحدًا لفئة واجهة المستخدم الخاصة بك ويعيد ذلك من 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; }
HandMenuKey
بعد ذلك ، قم بتوفير وظيفة HandleMenuKey()
، والتي تأخذ ضغطة مفتاح ورؤية القائمة الحالية ، وتقرر الإجراء الذي يجب اتخاذه:
int HandleMenuKey(int key, int visible) { if (visible) { switch (key) { case KEY_VOLUMEDOWN: return kHighlightDown; case KEY_VOLUMEUP: return kHighlightUp; case KEY_POWER: return kInvokeItem; } } return kNoAction; }
تأخذ الطريقة رمز مفتاح (تمت معالجته مسبقًا ووضعه في قائمة الانتظار بواسطة طريقة CheckKey()
لكائن واجهة المستخدم) ، والحالة الحالية لرؤية القائمة / سجل النص. القيمة المعادة هي عدد صحيح. إذا كانت القيمة 0 أو أعلى ، فسيتم أخذ ذلك على أنه موضع عنصر القائمة ، والذي يتم استدعاؤه على الفور (انظر طريقة InvokeMenuItem()
أدناه). وإلا يمكن أن يكون أحد الثوابت المحددة مسبقًا التالية:
- kHighlightUp . انقل تمييز القائمة إلى العنصر السابق
- k تسليط الضوء على الأسفل . انقل تمييز القائمة إلى العنصر التالي
- kInvokeItem . استدعاء العنصر المميز حاليًا
- kNoAction . لا تفعل شيئًا بضغطة المفتاح هذه
كما يتضح من الوسيطة المرئية ، يتم استدعاء HandleMenuKey()
حتى إذا كانت القائمة غير مرئية. بخلاف CheckKey()
، لا يتم استدعاؤه أثناء قيام الاسترداد بشيء مثل مسح البيانات أو تثبيت حزمة — يتم استدعاؤه فقط عندما يكون الاسترداد خاملاً وينتظر الإدخال.
آليات تراكبال
إذا كان جهازك يحتوي على آلية إدخال تشبه كرة التتبع (تنشئ أحداث إدخال من النوع EV_REL والرمز REL_Y) ، فإن الاسترداد يقوم بتوليف ضغطات المفاتيح KEY_UP و KEY_DOWN كلما أبلغ جهاز الإدخال الذي يشبه كرة التتبع عن الحركة في المحور Y. كل ما عليك فعله هو تعيين أحداث KEY_UP و KEY_DOWN في إجراءات القائمة. لا يحدث هذا التعيين لـ CheckKey()
، لذلك لا يمكنك استخدام حركات كرة التتبع كمحفزات لإعادة تشغيل الشاشة أو تبديلها.
مفاتيح التعديل
للتحقق من استمرار الضغط على المفاتيح كمعدِّلات ، اتصل IsKeyPressed()
لكائن واجهة المستخدم الخاص بك. على سبيل المثال ، في بعض الأجهزة ، يؤدي الضغط على Alt-W في الاسترداد إلى بدء مسح البيانات سواء كانت القائمة مرئية أم لا. يمكنك تنفيذ مثل هذا:
int HandleMenuKey(int key, int visible) { if (ui->IsKeyPressed(KEY_LEFTALT) && key == KEY_W) { return 2; // position of the "wipe data" item in the menu } ... }
ملاحظة: إذا كانت القيمة المرئية خاطئة ، فليس من المنطقي إرجاع القيم الخاصة التي تتلاعب بالقائمة (نقل التمييز ، استدعاء العنصر المميز) نظرًا لأن المستخدم لا يمكنه رؤية التمييز. ومع ذلك ، يمكنك إرجاع القيم إذا رغبت في ذلك.
InvokeMenuItem
بعد ذلك ، قم بتوفير أسلوب InvokeMenuItem()
الذي يقوم بتعيين مواضع عدد صحيح في صفيف العناصر التي يتم إرجاعها بواسطة GetMenuItems()
إلى الإجراءات. لمصفوفة العناصر في مثال التارديس ، استخدم:
BuiltinAction InvokeMenuItem(int menu_position) { switch (menu_position) { case 0: return REBOOT; case 1: return APPLY_ADB_SIDELOAD; case 2: return WIPE_DATA; case 3: return WIPE_CACHE; default: return NO_ACTION; } }
يمكن أن تعيد هذه الطريقة أي عضو في التعداد المدمج لإخبار النظام باتخاذ هذا الإجراء (أو العضو NO_ACTION إذا كنت تريد أن لا يقوم النظام بأي شيء). هذا هو المكان المناسب لتوفير وظائف استرداد إضافية تتجاوز ما هو موجود في النظام: أضف عنصرًا لها في قائمتك ، وقم بتنفيذه هنا عند استدعاء عنصر القائمة هذا ، وأعد NO_ACTION حتى لا يقوم النظام بأي شيء آخر.
يحتوي BuiltinAction على القيم التالية:
- NO_ACTION . لا تفعل شيئا.
- أعد التشغيل . اخرج من الاسترداد وأعد تشغيل الجهاز بشكل طبيعي.
- APPLY_EXT ، APPLY_CACHE ، APPLY_ADB_SIDELOAD . قم بتثبيت حزمة تحديث من أماكن مختلفة. للحصول على التفاصيل ، انظر Sideloading .
- WIPE_CACHE . أعد تهيئة قسم ذاكرة التخزين المؤقت فقط. لا يوجد تأكيد مطلوب لأن هذا غير ضار نسبيًا.
- WIPE_DATA . إعادة تنسيق أقسام بيانات المستخدم وذاكرة التخزين المؤقت ، المعروفة أيضًا باسم إعادة تعيين بيانات المصنع. يُطلب من المستخدم تأكيد هذا الإجراء قبل المتابعة.
الطريقة الأخيرة ، WipeData()
، اختيارية ويتم استدعاؤها عند بدء عملية مسح البيانات (إما من الاسترداد عبر القائمة أو عندما يختار المستخدم إجراء إعادة تعيين بيانات المصنع من النظام الرئيسي). يتم استدعاء هذه الطريقة قبل مسح بيانات المستخدم وأقسام ذاكرة التخزين المؤقت. إذا كان جهازك يخزن بيانات المستخدم في أي مكان بخلاف هذين القسمين ، فيجب عليك مسحها هنا. يجب عليك إرجاع 0 للإشارة إلى النجاح وقيمة أخرى للفشل ، على الرغم من تجاهل القيمة المعادة حاليًا. يتم مسح بيانات المستخدم وأقسام ذاكرة التخزين المؤقت سواء قمت بإرجاع النجاح أو الفشل.
int WipeData() { // ... do something tardis-specific here, if needed .... return 0; }
صنع الجهاز
أخيرًا ، قم بتضمين بعض النصوص المعيارية في نهاية ملف recovery_ui.cpp make_device()
التي تُنشئ وتُرجع مثيلاً لفئة جهازك:
class TardisDevice : public Device { // ... all the above methods ... }; Device* make_device() { return new TardisDevice(); }
بناء وربط لاستعادة الجهاز
بعد الانتهاء من ملف recovery_ui.cpp ، قم ببنائه وربطه بالاسترداد على جهازك. في Android.mk ، أنشئ مكتبة ثابتة تحتوي على ملف C ++ فقط:
device/yoyodyne/tardis/recovery/Android.mk
LOCAL_PATH := $(call my-dir) include $(CLEAR_VARS) LOCAL_MODULE_TAGS := eng LOCAL_C_INCLUDES += bootable/recovery LOCAL_SRC_FILES := recovery_ui.cpp # should match TARGET_RECOVERY_UI_LIB set in BoardConfig.mk LOCAL_MODULE := librecovery_ui_tardis include $(BUILD_STATIC_LIBRARY)
بعد ذلك ، في تكوين اللوحة لهذا الجهاز ، حدد مكتبتك الثابتة كقيمة TARGET_RECOVERY_UI_LIB.
device/yoyodyne/tardis/BoardConfig.mk [...] # device-specific extensions to the recovery UI TARGET_RECOVERY_UI_LIB := librecovery_ui_tardis
استرداد صور UI
تتكون واجهة مستخدم الاسترداد من الصور. من الناحية المثالية ، لا يتفاعل المستخدمون أبدًا مع واجهة المستخدم: أثناء التحديث العادي ، يبدأ الهاتف في الاسترداد ، ويملأ شريط تقدم التثبيت ، ويعيد التشغيل إلى النظام الجديد دون إدخال من المستخدم. في حالة حدوث مشكلة في تحديث النظام ، فإن إجراء المستخدم الوحيد الذي يمكن اتخاذه هو الاتصال بخدمة العملاء.
تغني واجهة الصور فقط عن الحاجة إلى الترجمة. ومع ذلك ، اعتبارًا من Android 5.0 ، يمكن أن يعرض التحديث سلسلة من النص (على سبيل المثال "تثبيت تحديث النظام ...") مع الصورة. لمزيد من التفاصيل ، راجع نص الاسترداد المترجم .
Android 5.0 والإصدارات الأحدث
تستخدم واجهة مستخدم Android 5.0 والإصدارات الأحدث للاسترداد صورتين رئيسيتين: صورة الخطأ ورسوم التثبيت المتحركة.
![]() الشكل 1. icon_error.png | ![]() الشكل 2. icon_installing.png |
يتم تمثيل الرسم المتحرك للتثبيت كصورة PNG واحدة مع إطارات مختلفة للرسوم المتحركة متشابكة مع صف (وهذا هو سبب ظهور الشكل 2 مضغوطًا). على سبيل المثال ، بالنسبة للرسوم المتحركة ذات السبعة إطارات 200 × 200 ، قم بإنشاء صورة واحدة مقاس 200 × 1400 حيث يكون الإطار الأول عبارة عن صفوف 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 وتعيين عدد الإطارات في الرسم المتحرك على 0 (رمز الخطأ ليس متحركًا ؛ إنه دائمًا صورة ثابتة).
الإصدار 4.x من Android والإصدارات الأقدم
تستخدم واجهة مستخدم Android 4.x والإصدارات السابقة للاسترداد صورة الخطأ (الموضحة أعلاه) ورسوم التثبيت المتحركة بالإضافة إلى العديد من صور التراكب:
![]() الشكل 3. icon_installing.png | ![]() الشكل 4. icon-install_overlay01.png |
![]() الشكل 5. icon_installing_overlay07.png |
أثناء التثبيت ، يتم إنشاء العرض على الشاشة من خلال رسم صورة icon_installing.png ، ثم رسم أحد إطارات التراكب أعلىها عند الإزاحة المناسبة. هنا ، يوجد مربع أحمر متراكب لإبراز مكان وضع التراكب أعلى الصورة الأساسية:
![]() الشكل 6. تثبيت إطار الرسوم المتحركة 1 (icon_installing.png + icon_installing_overlay01.png) | ![]() الشكل 7. تثبيت إطار الرسوم المتحركة 7 (icon_installing.png + icon_installing_overlay07.png) |
يتم عرض الإطارات اللاحقة من خلال رسم صورة التراكب التالية فقط فوق ما هو موجود بالفعل ؛ لم يتم إعادة رسم الصورة الأساسية.
يتم تعيين عدد الإطارات في الرسم المتحرك والسرعة المطلوبة وإزاحة x و y للتراكب بالنسبة إلى القاعدة بواسطة متغيرات الأعضاء لفئة ScreenRecoveryUI. عند استخدام صور مخصصة بدلاً من الصور الافتراضية ، تجاوز طريقة Init()
في الفئة الفرعية لتغيير هذه القيم لصورك المخصصة (للحصول على التفاصيل ، راجع ScreenRecoveryUI ). يمكن أن يساعد البرنامج النصي bootable/recovery/make-overlay.py
في تحويل مجموعة من إطارات الصور إلى نموذج "الصورة الأساسية + صور التراكب" التي يحتاجها الاسترداد ، بما في ذلك حساب التعويضات الضرورية.
توجد الصور الافتراضية في صورة bootable/recovery/res/images
. لاستخدام صورة ثابتة أثناء التثبيت ، تحتاج فقط إلى توفير صورة icon_installing.png وتعيين عدد الإطارات في الرسم المتحرك على 0 (رمز الخطأ ليس متحركًا ؛ إنه دائمًا صورة ثابتة).
نص الاسترداد المترجمة
يعرض الإصدار 5.x من نظام التشغيل Android سلسلة نصية (على سبيل المثال ، "تثبيت تحديث النظام ...") مع الصورة. عندما يبدأ النظام الرئيسي عملية الاسترداد ، فإنه يمرر الإعدادات المحلية الحالية للمستخدم كخيار سطر أوامر للاسترداد. لكل رسالة يتم عرضها ، يتضمن الاسترداد صورة مركبة ثانية مع سلاسل نصية معروضة مسبقًا لتلك الرسالة في كل لغة.
عينة صورة لسلاسل نص الاسترداد:

الشكل 8. نص مترجم لرسائل الاسترداد
يمكن أن يعرض نص الاسترداد الرسائل التالية:
- جاري تثبيت تحديث النظام ...
- خطأ!
- محو ... (عند إجراء مسح البيانات / إعادة ضبط المصنع)
- لا يوجد أمر (عندما يقوم المستخدم بالتمهيد إلى الاسترداد يدويًا)
تطبيق Android في bootable/recovery/tools/recovery_l10n/
يعرض ترجمات رسالة وينشئ الصورة المركبة. للحصول على تفاصيل حول استخدام هذا التطبيق ، راجع التعليقات في bootable/recovery/tools/recovery_l10n/src/com/android/recovery_l10n/Main.java
.
عندما يقوم المستخدم بالتمهيد إلى الاسترداد يدويًا ، قد لا تكون اللغة متاحة ولا يتم عرض أي نص. لا تجعل الرسائل النصية مهمة لعملية الاسترداد.
ملاحظة: الواجهة المخفية التي تعرض رسائل السجل وتسمح للمستخدم بتحديد الإجراءات من القائمة متاحة باللغة الإنجليزية فقط.
أشرطة التقدم
يمكن أن تظهر أشرطة التقدم أسفل الصورة الرئيسية (أو الرسوم المتحركة). يتكون شريط التقدم من خلال دمج صورتي إدخال ، والتي يجب أن تكون بنفس الحجم:

الشكل 9. progress_empty.png

الشكل 10. progress_fill.png
يتم عرض الطرف الأيسر من صورة التعبئة بجوار الطرف الأيمن للصورة الفارغة لعمل شريط التقدم. يتم تغيير موضع الحدود بين الصورتين للإشارة إلى التقدم. على سبيل المثال ، مع الأزواج المذكورة أعلاه من الصور المدخلة ، اعرض:

الشكل 11. شريط التقدم عند 1٪>

الشكل 12. شريط التقدم عند 10٪

الشكل 13. شريط التقدم عند 50٪
يمكنك توفير إصدارات خاصة بالجهاز من هذه الصور عن طريق وضعها في (في هذا المثال) device/yoyodyne/tardis/recovery/res/images
. يجب أن تتطابق أسماء الملفات مع الأسماء المذكورة أعلاه ؛ عند العثور على ملف في هذا الدليل ، يستخدمه نظام الإنشاء في تفضيل الصورة الافتراضية المقابلة. يتم دعم ملفات PNG فقط بتنسيق RGB أو RGBA بعمق ألوان 8 بت.
ملاحظة: في 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()
عليها.
افترض أن الوظيفة تحتاج إلى وسيطتين: مفتاح ذو قيمة سلسلة وصورة بقيمة blob. يمكنك قراءة الحجج مثل هذا:
Value* key = EvaluateValue(state, argv[0]); if (key == NULL) { return NULL; } if (key->type != VAL_STRING) { ErrorAbort(state, "first arg to %s() must be string", name); FreeValue(key); return NULL; } Value* image = EvaluateValue(state, argv[1]); if (image == NULL) { FreeValue(key); // must always free Value objects return NULL; } if (image->type != VAL_BLOB) { ErrorAbort(state, "second arg to %s() must be blob", name); FreeValue(key); FreeValue(image) return NULL; }
يمكن أن يصبح التحقق من القيمة NULL وتحرير الوسيطات التي تم تقييمها مسبقًا مملاً لعدة وسيطات. يمكن أن تسهل الدالة ReadValueArgs()
ذلك. بدلاً من الكود أعلاه ، كان من الممكن أن تكتب هذا:
Value* key; Value* image; if (ReadValueArgs(state, argv, 2, &key, &image) != 0) { return NULL; // ReadValueArgs() will have set the error message } if (key->type != VAL_STRING || image->type != VAL_BLOB) { ErrorAbort(state, "arguments to %s() have wrong type", name); FreeValue(key); FreeValue(image) return NULL; }
لا يقوم ReadValueArgs()
بفحص النوع ، لذا يجب القيام بذلك هنا ؛ من الأنسب القيام بذلك باستخدام عبارة if واحدة على حساب إنتاج رسالة خطأ أقل تحديدًا إلى حد ما عندما تفشل. لكن ReadValueArgs()
يتعامل مع تقييم كل وسيطة وتحرير جميع الوسائط التي تم تقييمها مسبقًا (بالإضافة إلى تعيين رسالة خطأ مفيدة) في حالة فشل أي من التقييمات. يمكنك استخدام دالة ملائمة ReadValueVarArgs()
لتقييم عدد متغير من الوسائط (تقوم بإرجاع مصفوفة Value*
).
بعد تقييم الحجج ، قم بعمل الوظيفة:
// key->data is a NUL-terminated string // image->data and image->size define a block of binary data // // ... some device-specific magic here to // reprogram the tardis using those two values ...
يجب أن تكون القيمة المعادة كائن Value*
؛ ستنتقل ملكية هذا الكائن إلى المتصل. يأخذ المتصل ملكية أي بيانات مشار إليها بهذه Value*
- على وجه التحديد عضو البيانات.
في هذه الحالة ، تريد إرجاع قيمة صواب أو خطأ للإشارة إلى النجاح. تذكر أن الجملة الفارغة خاطئة وأن جميع الجمل الأخرى صحيحة . يجب عليك malloc كائن قيمة مع نسخة malloc'd من السلسلة الثابتة للعودة ، لأن المتصل سوف 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 هو اسم المكتبة الثابتة التي تحتوي على هذا الرمز. Call 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 (والتي قد تحتوي على مكتبات متعددة ؛ يتم تسجيلها جميعًا). إذا كانت التعليمات البرمجية الخاصة بك تعتمد على مكتبات ثابتة أخرى ليست هي نفسها ملحقات تعديل (على سبيل المثال ، ليس لديها وظيفة 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 الخاص بك ، قد يحتوي البرنامج النصي للتحديث على: 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
هذه تسمى ملفات الراديو لأسباب تاريخية ؛ قد لا علاقة لهم براديو الجهاز (إن وجد). إنها ببساطة كتل بيانات مبهمة ينسخها نظام الإنشاء إلى ملفات الهدف .zip التي تستخدمها أدوات إنشاء OTA. عندما تقوم بإنشاء ، يتم تخزين tardis.dat في target-files.zip كـ RADIO/tardis.dat
. يمكنك استدعاء add-radio-file
عدة مرات لإضافة العديد من الملفات التي تريدها.
وحدة بايثون
لتوسيع أدوات الإصدار ، اكتب وحدة 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()
- تم استدعاؤه بالقرب من بداية إنشاء OTA كامل. يعد هذا مكانًا جيدًا لإرسال تأكيدات حول الحالة الحالية للجهاز. لا ترسل أوامر نصية تُجري تغييرات على الجهاز.
-
FullOTA_InstallBegin()
- يتم استدعاؤه بعد مرور جميع التأكيدات المتعلقة بحالة الجهاز ولكن قبل إجراء أي تغييرات. يمكنك إصدار أوامر للتحديثات الخاصة بالجهاز والتي يجب تشغيلها قبل تغيير أي شيء آخر على الجهاز.
-
FullOTA_InstallEnd()
- يتم استدعاؤه في نهاية إنشاء البرنامج النصي ، بعد أن تم إصدار أوامر البرنامج النصي لتحديث أقسام التمهيد والنظام. يمكنك أيضًا إصدار أوامر إضافية للتحديثات الخاصة بالجهاز.
-
IncrementalOTA_Assertions()
- مشابه لـ
FullOTA_Assertions()
ولكن يتم استدعاؤه عند إنشاء حزمة تحديث تزايدي. -
IncrementalOTA_VerifyBegin()
- يتم استدعاؤه بعد مرور جميع التأكيدات المتعلقة بحالة الجهاز ولكن قبل إجراء أي تغييرات. يمكنك إصدار أوامر للتحديثات الخاصة بالجهاز والتي يجب تشغيلها قبل تغيير أي شيء آخر على الجهاز.
-
IncrementalOTA_VerifyEnd()
- يتم استدعاؤها في نهاية مرحلة التحقق ، عندما ينتهي البرنامج النصي من تأكيد الملفات التي سيتطرق إليها ، تحتوي على محتويات البداية المتوقعة. في هذه المرحلة ، لم يتم تغيير أي شيء على الجهاز. يمكنك أيضًا إرسال رمز لعمليات تحقق إضافية خاصة بالجهاز.
-
IncrementalOTA_InstallBegin()
- تم استدعاؤه بعد أن تم التحقق من أن الملفات المراد تصحيحها تحتوي على الحالة السابقة المتوقعة ولكن قبل إجراء أي تغييرات. يمكنك إصدار أوامر للتحديثات الخاصة بالجهاز والتي يجب تشغيلها قبل تغيير أي شيء آخر على الجهاز.
-
IncrementalOTA_InstallEnd()
- على غرار نظيرتها الكاملة في حزمة OTA ، يُطلق عليها في نهاية إنشاء البرنامج النصي ، بعد إصدار أوامر البرنامج النصي لتحديث أقسام التمهيد والنظام. يمكنك أيضًا إصدار أوامر إضافية للتحديثات الخاصة بالجهاز.
ملاحظة: إذا انقطع الجهاز عن الطاقة ، فقد تتم إعادة تشغيل تثبيت OTA من البداية. كن مستعدًا للتعامل مع الأجهزة التي تم تشغيل هذه الأوامر عليها بالفعل ، كليًا أو جزئيًا.
وظائف تمرير إلى كائنات المعلومات
قم بتمرير الوظائف إلى كائن معلومات واحد يحتوي على عناصر مفيدة متنوعة:
- info.input_zip . (OTAs الكاملة فقط) الكائن
zipfile.ZipFile
الهدف الإدخال .zip. - info.source_zip . (OTAs تزايدي فقط) كائن
zipfile.ZipFile
المصدر الهدف .zip (البناء موجود بالفعل على الجهاز عند تثبيت الحزمة المتزايدة). - info.target_zip . (OTAs تزايدي فقط) كائن
zipfile.ZipFile
الهدف الهدف .zip (بناء الحزمة المتزايدة التي تضعها على الجهاز). - info.output_zip . يتم إنشاء الحزمة ؛ تم فتح كائن
zipfile.ZipFile
للكتابة. استخدم common.ZipWriteStr (info.output_zip ، اسم الملف ، البيانات ) لإضافة ملف إلى الحزمة. - info.script . كائن البرنامج النصي الذي يمكنك إلحاق الأوامر به. Call
info.script.AppendExtra( script_text )
لإخراج النص في البرنامج النصي. تأكد من أن نص الإخراج ينتهي بفاصلة منقوطة حتى لا يتعارض مع الأوامر الصادرة بعد ذلك.
للحصول على تفاصيل حول كائن المعلومات ، راجع وثائق Python Software Foundation لأرشيفات 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
، فإنها تلتقط تلقائيًا الوحدة الخاصة بالجهاز من ملف 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
.
تحميل الجانب
يحتوي الاسترداد على آلية تحميل جانبي لتثبيت حزمة التحديث يدويًا دون تنزيلها عبر الهواء بواسطة النظام الرئيسي. يعد Sideloading مفيدًا لتصحيح الأخطاء أو إجراء تغييرات على الأجهزة التي لا يمكن تمهيد النظام الرئيسي فيها.
تاريخياً ، تم التحميل الجانبي من خلال تحميل الحزم من بطاقة SD الخاصة بالجهاز ؛ في حالة وجود جهاز غير قابل للتمهيد ، يمكن وضع الحزمة على بطاقة SD باستخدام بعض أجهزة الكمبيوتر الأخرى ثم إدخال بطاقة SD في الجهاز. لاستيعاب أجهزة Android بدون وحدة تخزين خارجية قابلة للإزالة ، يدعم الاسترداد آليتين إضافيتين للتحميل الجانبي: تحميل الحزم من قسم ذاكرة التخزين المؤقت ، وتحميلها عبر USB باستخدام adb.
لاستدعاء كل آلية تحميل جانبي ، يمكن لطريقة Device::InvokeMenuItem()
الخاصة بجهازك إرجاع القيم التالية لـ BuiltinAction:
- APPLY_EXT . Sideload حزمة تحديث من وحدة التخزين الخارجية (
/sdcard
). يجب أن تحدد recovery.fstab نقطة تحميل/sdcard
. This is not usable on devices that emulate an SD card with a symlink to/data
(or some similar mechanism)./data
is typically not available to recovery because it may be encrypted. The recovery UI displays a menu of .zip files in/sdcard
and allows the user to select one. - APPLY_CACHE . Similar to loading a package from
/sdcard
except that the/cache
directory (which is always available to recovery) is used instead. From the regular system,/cache
is only writable by privileged users, and if the device isn't bootable then the/cache
directory 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
sideload
work (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.