يتضمّن نظام الاسترداد عدة نقاط ربط لإدراج رمز خاص بالجهاز، ما يتيح لتحديثات OTA تعديل أجزاء أخرى من الجهاز غير نظام Android (مثل النطاق الأساسي أو معالج الراديو).
تخصّص الأقسام والأمثلة التالية الجهاز tardis الذي تنتجه الشركة yoyodyne.
خريطة الأقسام
بدءًا من الإصدار 2.3 من نظام التشغيل Android، تتوافق المنصة مع أجهزة فلاش 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
-
نظام ملفات yaffs2 على جهاز MTD flash. يجب أن يكون "device" هو اسم قسم MTD
ويجب أن يظهر في
/proc/mtd
. - mtd
-
قسم MTD أولي، يُستخدم للأقسام القابلة للتشغيل، مثل قسمي التمهيد والاسترداد. لا يتم في الواقع ربط MTD، ولكن يتم استخدام نقطة الربط كمفتاح لتحديد موقع القسم. يجب أن يكون "device"
هو اسم قسم MTD في
/proc/mtd
. - ext4
- نظام ملفات ext4 على جهاز فلاش eMMc يجب أن يكون "device" هو مسار جهاز الحظر.
- emmc
- جهاز حظر eMMC الأولي، ويُستخدم للأقسام القابلة للتشغيل، مثل أقسام التشغيل والاسترداد. على غرار نوع mtd، لا يتم ربط eMMc مطلقًا، ولكن يتم استخدام سلسلة نقطة الربط لتحديد موقع الجهاز في الجدول.
- vfat
-
نظام ملفات FAT فوق جهاز حظر، وعادةً ما يكون ذلك للتخزين الخارجي مثل بطاقة SD. الجهاز هو جهاز الحظر، والجهاز 2 هو جهاز حظر ثانٍ يحاول النظام تركيبه في حال تعذّر تركيب الجهاز الأساسي (للتوافق مع بطاقات SD التي قد يتم أو لا يتم تنسيقها باستخدام جدول أقسام).
يجب تثبيت جميع الأقسام في الدليل الجذر (أي يجب أن تبدأ قيمة نقطة التثبيت بشرطة مائلة وألا تحتوي على أي شرطات مائلة أخرى). لا ينطبق هذا القيد إلا على تثبيت أنظمة الملفات في وضع الاسترداد، ويمكن للنظام الرئيسي تثبيتها في أي مكان. يجب أن تكون الدلائل
/boot
و/recovery
و/misc
من الأنواع الأولية (mtd أو emmc)، بينما يجب أن تكون الدلائل/system
و/data
و/cache
و/sdcard
(إذا كانت متاحة) من أنواع نظام الملفات (yaffs2 أو ext4 أو vfat).
بدءًا من الإصدار 3.0 من نظام التشغيل Android، يكتسب ملف recovery.fstab حقلًا اختياريًا إضافيًا، وهو options. الخيار الوحيد المحدّد حاليًا هو الطول ، والذي يتيح لك تحديد طول القسم بشكل صريح. يتم استخدام هذا الطول عند إعادة تنسيق القسم (على سبيل المثال، لقسم بيانات المستخدم أثناء عملية محو البيانات أو إعادة الضبط على الإعدادات الأصلية، أو لقسم النظام أثناء تثبيت حزمة OTA كاملة). إذا كانت قيمة الطول سالبة، يتم تحديد حجم التنسيق من خلال إضافة قيمة الطول إلى حجم القسم الفعلي. على سبيل المثال، يعني ضبط "length=-16384" أنّه لن تتم الكتابة فوق آخر 16 ألف بايت من هذا القسم عند إعادة تنسيقه. يتيح ذلك استخدام ميزات مثل تشفير قسم بيانات المستخدم (حيث يتم تخزين البيانات الوصفية للتشفير في نهاية القسم الذي يجب عدم الكتابة فوقه).
ملاحظة: الحقلان device2 وoptions اختياريان، ما يؤدي إلى حدوث غموض في عملية التحليل. إذا كان الإدخال في الحقل الرابع على السطر يبدأ بالحرف "/"، يُعدّ إدخال device2، وإذا كان الإدخال لا يبدأ بالحرف "/"، يُعدّ حقل خيارات.
صورة بدء التشغيل المتحركة
بإمكان الشركات المصنّعة للأجهزة تخصيص الصورة المتحركة التي تظهر عند بدء تشغيل جهاز 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 };
ملاحظة: يتم اقتطاع الأسطر الطويلة (بدلاً من التفافها)، لذا يجب مراعاة عرض شاشة جهازك.
Customize CheckKey
بعد ذلك، حدِّد تنفيذ RecoveryUI لجهازك. يفترض هذا المثال أنّ الجهاز
tardis مزوّد بشاشة، لذا يمكنك الاستفادة من
ScreenRecoveryUIimplementation المضمّنة (راجِع التعليمات الخاصة
بالأجهزة غير المزوّدة بشاشة). الوظيفة الوحيدة التي يمكن تخصيصها من ScreenRecoveryUI هي CheckKey()
، وهي تتولّى معالجة المفاتيح غير المتزامنة الأولية:
class TardisUI : public ScreenRecoveryUI { public: virtual KeyAction CheckKey(int key) { if (key == KEY_HOME) { return TOGGLE; } return ENQUEUE; } };
الثوابت الرئيسية
يتم تحديد الثوابت KEY_* في linux/input.h
.يتم استدعاء CheckKey()
بغض النظر عمّا يحدث في بقية عملية الاسترداد: عند إيقاف القائمة، وعندما تكون القائمة مفعّلة، وأثناء تثبيت الحزمة، وأثناء محو بيانات المستخدم، وما إلى ذلك. ويمكن أن تعرض إحدى الثوابت الأربعة التالية:
- زر التبديل تفعيل عرض القائمة و/أو سجلّ النصوص أو إيقافه
- إعادة التشغيل إعادة تشغيل الجهاز فورًا
- تجاهل تجاهُل الضغط على المفتاح
- ENQUEUE إضافة ضغطة المفتاح هذه إلى قائمة الانتظار ليتم استخدامها بشكل متزامن (أي من خلال نظام قائمة الاسترداد إذا كانت الشاشة مفعّلة)
يتم استدعاء CheckKey()
في كل مرة يتبع فيها حدث الضغط على المفتاح حدث رفع المفتاح للمفتاح نفسه. (لا يؤدي تسلسل الأحداث A-down B-down B-up A-up إلا إلى استدعاء CheckKey(B)
.) يمكن أن تستدعي CheckKey()
الدالة
IsKeyPressed()
لمعرفة ما إذا كان يتم الضغط على مفاتيح أخرى. (في تسلسل الأحداث الرئيسية أعلاه، إذا تم استدعاء CheckKey(B)
IsKeyPressed(A)
، كان سيتم عرض القيمة "صحيح").
يمكن أن تحتفظ CheckKey()
بالحالة في فئتها، وقد يكون ذلك مفيدًا لرصد تسلسلات المفاتيح. يوضّح هذا المثال عملية إعداد أكثر تعقيدًا قليلاً: يتم تبديل الشاشة من خلال الضغط مع الاستمرار على زر التشغيل والضغط على زر رفع مستوى الصوت، ويمكن إعادة تشغيل الجهاز على الفور من خلال الضغط على زر التشغيل خمس مرات متتالية (بدون الضغط على أي مفاتيح أخرى):
class TardisUI : public ScreenRecoveryUI { private: int consecutive_power_keys; public: TardisUI() : consecutive_power_keys(0) {} virtual KeyAction CheckKey(int key) { if (IsKeyPressed(KEY_POWER) && key == KEY_VOLUMEUP) { return TOGGLE; } if (key == KEY_POWER) { ++consecutive_power_keys; if (consecutive_power_keys >= 5) { return REBOOT; } } else { consecutive_power_keys = 0; } return ENQUEUE; } };
ScreenRecoveryUI
عند استخدام صورك الخاصة (رمز الخطأ، ومؤثر التثبيت، وأشرطة التقدم) مع ScreenRecoveryUI، يمكنك ضبط المتغيّر animation_fps
للتحكّم في سرعة المؤثرات الحركية باللقطات في الثانية.
ملاحظة: يتيح لك النص البرمجي الحالي interlace-frames.py
تخزين معلومات animation_fps
في الصورة نفسها. في الإصدارات السابقة من Android، كان من الضروري ضبط animation_fps
بنفسك.
لضبط المتغيّر animation_fps
، عليك إلغاء الدالة ScreenRecoveryUI::Init()
في الفئة الفرعية. اضبط القيمة، ثم استدعِ الدالة
parent Init()
لإكمال عملية الإعداد. تتوافق القيمة التلقائية (20 لقطة في الثانية) مع صور الاسترداد التلقائية، ولا تحتاج إلى توفير الدالة Init()
عند استخدام هذه الصور. لمزيد من التفاصيل حول الصور، يُرجى الاطّلاع على
صور واجهة مستخدم الاسترداد.
فئة الجهاز
بعد تنفيذ RecoveryUI، حدِّد فئة جهازك (التي تم تصنيفها فرعيًا من فئة الجهاز المضمّنة). يجب أن تنشئ هذه الدالة مثيلاً واحدًا لفئة واجهة المستخدم وتعرضه من الدالة GetUI()
:
class TardisDevice : public Device { private: TardisUI* ui; public: TardisDevice() : ui(new TardisUI) { } RecoveryUI* GetUI() { return ui; }
StartRecovery
يتم استدعاء الطريقة StartRecovery()
في بداية عملية الاسترداد، بعد تهيئة واجهة المستخدم وبعد تحليل الوسيطات، ولكن قبل اتّخاذ أي إجراء. لا ينفّذ التنفيذ التلقائي أي إجراء، لذا ليس عليك توفير ذلك في الفئة الفرعية إذا لم يكن لديك أي إجراء لتنفيذه:
void StartRecovery() { // ... do something tardis-specific here, if needed .... }
قائمة "الإمداد والإدارة" في قائمة الاسترداد
يستدعي النظام طريقتَين للحصول على قائمة أسطر العناوين وقائمة العناصر. في هذا التنفيذ، يتم عرض الصفائف الثابتة المحدّدة في أعلى الملف:
const char* const* GetMenuHeaders() { return HEADERS; } const char* const* GetMenuItems() { return ITEMS; }
HandleMenuKey
بعد ذلك، قدِّم HandleMenuKey()
دالة تأخذ ضغطة مفتاح وحالة ظهور القائمة الحالية، وتحدّد الإجراء الذي يجب اتّخاذه:
int HandleMenuKey(int key, int visible) { if (visible) { switch (key) { case KEY_VOLUMEDOWN: return kHighlightDown; case KEY_VOLUMEUP: return kHighlightUp; case KEY_POWER: return kInvokeItem; } } return kNoAction; }
تتلقّى الطريقة رمز مفتاح (تمت معالجته وإضافته إلى قائمة الانتظار سابقًا بواسطة
الطريقة CheckKey()
لعنصر واجهة المستخدم)، والحالة الحالية لإذن الدخول إلى القائمة/سجلّ النصوص. القيمة المعروضة هي عدد صحيح. إذا كانت القيمة 0 أو أعلى، يتم اعتبارها موضع عنصر قائمة يتم استدعاؤه على الفور (راجِع طريقة InvokeMenuItem()
أدناه). بخلاف ذلك، يمكن أن تكون إحدى الثوابت المحدّدة مسبقًا التالية:
- kHighlightUp نقل التمييز في القائمة إلى العنصر السابق
- kHighlightDown. نقل تمييز القائمة إلى العنصر التالي
- kInvokeItem استدعاء العنصر المميّز حاليًا
- kNoAction عدم تنفيذ أي إجراء عند الضغط على المفتاح
وكما هو موضح في الوسيطة المرئية، يتم استدعاء HandleMenuKey()
حتى إذا لم تكن القائمة مرئية. على عكس CheckKey()
، لا يتم استدعاؤه أثناء تنفيذ عملية الاسترداد، مثل محو البيانات أو تثبيت حزمة، بل يتم استدعاؤه فقط عندما تكون عملية الاسترداد في وضع الخمول وتنتظر إدخال بيانات.
آليات كرة التعقّب
إذا كان جهازك يتضمّن آلية إدخال شبيهة بكرة التتبّع (تنشئ أحداث إدخال من النوع EV_REL
والرمز REL_Y)، فإنّ عملية الاسترداد تنشئ ضغطات على المفتاحين KEY_UP وKEY_DOWN كلما
أبلغ جهاز الإدخال الشبيه بكرة التتبّع عن حركة في المحور Y. كل ما عليك فعله هو ربط حدثَي KEY_UP وKEY_DOWN بإجراءات القائمة. لا يحدث هذا التعيين في CheckKey()
، لذا لا يمكنك استخدام حركات كرة التتبّع كإشارات لإعادة التشغيل أو تبديل العرض.
مفاتيح التعديل
للتحقّق من الضغط مع الاستمرار على المفاتيح كمفاتيح تعديل، استدعِ طريقة IsKeyPressed()
الخاصة بكائن واجهة المستخدم. على سبيل المثال، عند الضغط على Alt-W في وضع الاسترداد على بعض الأجهزة، ستبدأ عملية محو البيانات سواء كانت القائمة مرئية أم لا. يمكنك تنفيذ ذلك على النحو التالي:
int HandleMenuKey(int key, int visible) { if (ui->IsKeyPressed(KEY_LEFTALT) && key == KEY_W) { return 2; // position of the "wipe data" item in the menu } ... }
ملاحظة: إذا كانت قيمة visible هي false، لن يكون من المنطقي عرض القيم الخاصة التي تتحكّم في القائمة (نقل التمييز، استدعاء العنصر المميّز) لأنّ المستخدم لن يتمكّن من رؤية التمييز. ومع ذلك، يمكنك إرجاع القيم إذا أردت ذلك.
InvokeMenuItem
بعد ذلك، قدِّم طريقة InvokeMenuItem()
تربط بين مواضع الأعداد الصحيحة في مصفوفة العناصر التي تعرضها GetMenuItems()
والإجراءات. بالنسبة إلى مصفوفة العناصر في مثال 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 تثبيت حزمة تحديث من مصادر مختلفة لمزيد من التفاصيل، يُرجى الاطّلاع على التثبيت الجانبي.
- 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
صور واجهة مستخدم الاسترداد
تتألف واجهة مستخدم وضع الاسترداد من صور. من المفترض ألا يتفاعل المستخدمون مع واجهة المستخدم مطلقًا: أثناء التحديث العادي، يتم تشغيل الهاتف في وضع الاسترداد، ويتم ملء شريط تقدّم التثبيت، ثم يتم إعادة تشغيل النظام الجديد بدون أي إجراء من المستخدم. في حال حدوث مشكلة في تحديث النظام، يكون الإجراء الوحيد الذي يمكن للمستخدم اتخاذه هو الاتصال بفريق خدمة العملاء.
تغني الواجهة التي تعرض الصور فقط عن الحاجة إلى توفيرها بلغات مختلفة. ومع ذلك، بدءًا من الإصدار 5.0 من نظام التشغيل Android، يمكن أن يعرض التحديث سلسلة نصية (مثل "جارٍ تثبيت تحديث النظام...") مع الصورة. لمزيد من التفاصيل، يُرجى الاطّلاع على نص الاسترداد المترجَم.
Android 5.0 والإصدارات الأحدث
تستخدم واجهة مستخدم الاسترداد في الإصدار 5.0 من نظام التشغيل Android والإصدارات الأحدث صورتَين رئيسيتَين: صورة الخطأ ورسم التثبيت المتحرك.
![]() الشكل 1. icon_error.png |
![]() الشكل 2. icon_installing.png |
يتم تمثيل الرسوم المتحركة الخاصة بالتثبيت كصورة PNG واحدة تتضمّن لقطات مختلفة من الرسوم المتحركة متداخلة حسب الصف (وهذا هو السبب في أنّ الشكل 2 يبدو مضغوطًا). على سبيل المثال، لإنشاء صورة متحركة من سبعة إطارات بحجم 200x200، أنشئ صورة واحدة بحجم 200x1400 يكون فيها الإطار الأول هو الصفوف 0 و7 و14 و21 وما إلى ذلك، والإطار الثاني هو الصفوف 1 و8 و15 و22 وما إلى ذلك. تتضمّن الصورة المدمجة جزءًا نصيًا يشير إلى عدد إطارات الصورة المتحركة وعدد الإطارات في الثانية (FPS). تتلقّى الأداة bootable/recovery/interlace-frames.py
مجموعة من إطارات الإدخال
وتدمجها في الصورة المركّبة اللازمة التي تستخدمها عملية الاسترداد.
تتوفّر الصور التلقائية بكثافات مختلفة، وتقع في
bootable/recovery/res-$DENSITY/images
(مثلاً،
bootable/recovery/res-hdpi/images
). لاستخدام صورة ثابتة أثناء التثبيت، ما عليك سوى توفير الصورة icon_installing.png وضبط عدد اللقطات في الصورة المتحركة على 0 (رمز الخطأ ليس صورة متحركة، بل هو دائمًا صورة ثابتة).
الإصدار 4.x من نظام التشغيل Android والإصدارات الأقدم
تستخدم واجهة مستخدم الاسترداد في الإصدار 4.x من نظام التشغيل Android والإصدارات الأقدم صورة الخطأ (الموضّحة أعلاه) ورسوم التثبيت المتحركة بالإضافة إلى عدة صور متراكبة:
![]() الشكل 3. icon_installing.png |
![]() الشكل 4. icon-installing_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، إذا كانت اللغة معروفة في وضع الاسترداد وكانت لغة مكتوبة من اليمين إلى اليسار (مثل العربية والعبرية وغيرها)، سيتم ملء شريط التقدم من اليمين إلى اليسار.
الأجهزة غير المزودة بشاشات
لا تحتوي بعض أجهزة Android على شاشات. إذا كان جهازك عبارة عن جهاز بدون شاشة أو يتضمّن واجهة صوتية فقط، قد تحتاج إلى تخصيص واجهة المستخدم الخاصة باسترداد البيانات بشكلٍ أكثر شمولاً. بدلاً من إنشاء فئة فرعية من ScreenRecoveryUI، أنشئ فئة فرعية من فئتها الرئيسية RecoveryUI مباشرةً.
تتضمّن RecoveryUI طرقًا للتعامل مع عمليات واجهة المستخدم ذات المستوى الأدنى، مثل "تبديل العرض" و"تعديل شريط التقدم" و"عرض القائمة" و"تغيير اختيار القائمة" وما إلى ذلك. ويمكنك إلغاء هذه الطرق لتوفير واجهة مناسبة لجهازك. قد يحتوي جهازك على مصابيح LED يمكنك استخدام ألوان أو أنماط مختلفة من الوميض للإشارة إلى الحالة، أو قد تتمكّن من تشغيل الصوت. (ربما لا تريد توفير قائمة أو وضع "عرض النص" على الإطلاق، ويمكنك منع الوصول إليهما من خلال تنفيذ CheckKey()
وHandleMenuKey()
اللذين لا يفعّلان العرض أو يحدّدان عنصر قائمة أبدًا. في هذه الحالة، يمكن أن تكون العديد من طرق RecoveryUI التي عليك توفيرها مجرد رموز فارغة.)
راجِع 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); }
لم يتم تقييم وسيطاتك في الوقت الذي تم فيه استدعاء الدالة، بل يحدّد منطق الدالة أيًّا من هذه الوسيطات سيتم تقييمه وعدد مرات تقييمه. وبالتالي، يمكنك استخدام دوال الإضافة لتنفيذ بنى التحكّم الخاصة بك. Call Evaluate()
لتقييم
الوسيطة Expr*
، مع عرض Value*
. إذا عرضت الدالة Evaluate()
القيمة NULL، عليك تحرير أي موارد تستخدمها وعرض القيمة NULL على الفور (يؤدي ذلك إلى
نقل عمليات الإلغاء إلى أعلى حزمة edify). بخلاف ذلك، ستتولّى ملكية القيمة المعروضة وستكون مسؤولاً عن استدعاء FreeValue()
في النهاية.
لنفترض أنّ الدالة تحتاج إلى وسيطتَين: مفتاح بقيمة سلسلة وصورة بقيمة blob. يمكنك قراءة الوسيطات على النحو التالي:
Value* key = EvaluateValue(state, argv[0]); if (key == NULL) { return NULL; } if (key->type != VAL_STRING) { ErrorAbort(state, "first arg to %s() must be string", name); FreeValue(key); return NULL; } Value* image = EvaluateValue(state, argv[1]); if (image == NULL) { FreeValue(key); // must always free Value objects return NULL; } if (image->type != VAL_BLOB) { ErrorAbort(state, "second arg to %s() must be blob", name); FreeValue(key); FreeValue(image) return NULL; }
قد يصبح التحقّق من القيمة NULL وتحرير الوسيطات التي تم تقييمها سابقًا أمرًا شاقًا عند التعامل مع وسيطات متعدّدة. يمكن أن تسهّل الدالة ReadValueArgs()
هذه العملية. بدلاً من الرمز
أعلاه، كان بإمكانك كتابة ما يلي:
Value* key; Value* image; if (ReadValueArgs(state, argv, 2, &key, &image) != 0) { return NULL; // ReadValueArgs() will have set the error message } if (key->type != VAL_STRING || image->type != VAL_BLOB) { ErrorAbort(state, "arguments to %s() have wrong type", name); FreeValue(key); FreeValue(image) return NULL; }
لا يجري ReadValueArgs()
عملية التحقّق من النوع، لذا عليك إجراء ذلك هنا. من الأسهل إجراء ذلك باستخدام عبارة if واحدة، ولكن سيؤدي ذلك إلى ظهور رسالة خطأ أقل تحديدًا عند حدوث خطأ. ولكن ReadValueArgs()
تعالج تقييم كل وسيطة وتحرير جميع الوسيطات التي تم تقييمها سابقًا (بالإضافة إلى ضبط رسالة خطأ مفيدة) في حال تعذُّر أي من عمليات التقييم. يمكنك استخدام دالة
ReadValueVarArgs()
ملائمة لتقييم عدد متغيّر من الوسيطات (تعرض هذه الدالة صفيفًا من Value*
).
بعد تقييم الوسيطات، نفِّذ عمل الدالة:
// key->data is a NUL-terminated string // image->data and image->size define a block of binary data // // ... some device-specific magic here to // reprogram the tardis using those two values ...
يجب أن تكون القيمة المعروضة عبارة عن عنصر Value*
، وستنتقل ملكية هذا العنصر إلى المتصل. يتحمّل المتصل مسؤولية أي بيانات يشير إليها هذا
Value*
، وتحديدًا datamember.
في هذه الحالة، عليك عرض قيمة "صحيح" أو "خطأ" للإشارة إلى النجاح. تذكَّر أنّ السلسلة الفارغة هي false وأنّ جميع السلاسل الأخرى هي true. يجب تخصيص مساحة في الذاكرة لكائن 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()
سلسلة في كائن 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 لربطها بأداة التحديث بدون استدعاء
وظيفة التسجيل (غير المتوفّرة). على سبيل المثال، إذا كان الرمز البرمجي الخاص بجهازك يريد استخدام
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
لتوسيع أدوات الإصدار، اكتب وحدة Python (يجب أن يكون اسمها releasetools.py) يمكن للأدوات استدعاؤها إذا كانت متوفّرة. مثال:
device/yoyodyne/tardis/releasetools.py
import common def FullOTA_InstallEnd(info): # copy the data into the package. tardis_dat = info.input_zip.read("RADIO/tardis.dat") common.ZipWriteStr(info.output_zip, "tardis.dat", tardis_dat) # emit the script code to install this data on the device info.script.AppendExtra( """tardis.reprogram("the-key", package_extract_file("tardis.dat"));""")
تتولّى دالة منفصلة معالجة حالة إنشاء حزمة OTA تزايدية. في هذا المثال، لنفترض أنّك بحاجة إلى إعادة برمجة آلة الزمن فقط عندما يتغيّر الملف tardis.dat بين إصدارَين.
def IncrementalOTA_InstallEnd(info): # copy the data into the package. source_tardis_dat = info.source_zip.read("RADIO/tardis.dat") target_tardis_dat = info.target_zip.read("RADIO/tardis.dat") if source_tardis_dat == target_tardis_dat: # tardis.dat is unchanged from previous build; no # need to reprogram it return # include the new tardis.dat in the OTA package common.ZipWriteStr(info.output_zip, "tardis.dat", target_tardis_dat) # emit the script code to install this data on the device info.script.AppendExtra( """tardis.reprogram("the-key", package_extract_file("tardis.dat"));""")
وظائف الوحدة
يمكنك توفير الوظائف التالية في الوحدة (لا تنفِّذ سوى الوظائف التي تحتاج إليها).
FullOTA_Assertions()
- يتم استدعاؤها بالقرب من بداية إنشاء حزمة OTA كاملة. هذا مكان مناسب لإصدار تأكيدات بشأن الحالة الحالية للجهاز. لا تصدر أوامر نصية برمجية تُجري تغييرات على الجهاز.
FullOTA_InstallBegin()
- يتم استدعاء هذه الطريقة بعد اجتياز جميع التأكيدات بشأن حالة الجهاز ولكن قبل إجراء أي تغييرات. يمكنك إصدار أوامر لتحديثات خاصة بالجهاز يجب تنفيذها قبل إجراء أي تغييرات أخرى على الجهاز.
FullOTA_InstallEnd()
- يتم استدعاء هذه الدالة في نهاية عملية إنشاء البرنامج النصي، بعد إصدار أوامر البرنامج النصي لتعديل أقسام التشغيل والنظام. يمكنك أيضًا إصدار أوامر إضافية لتحديثات خاصة بالجهاز.
IncrementalOTA_Assertions()
-
تشبه هذه السمة
FullOTA_Assertions()
ولكن يتم استدعاؤها عند إنشاء حزمة تحديث تدريجي. IncrementalOTA_VerifyBegin()
- يتم استدعاء هذه الطريقة بعد اجتياز جميع التأكيدات بشأن حالة الجهاز ولكن قبل إجراء أي تغييرات. يمكنك إصدار أوامر لتحديثات خاصة بالجهاز يجب تنفيذها قبل إجراء أي تغييرات أخرى على الجهاز.
IncrementalOTA_VerifyEnd()
- يتم استدعاء هذه الدالة في نهاية مرحلة التحقّق، أي بعد أن ينتهي النص البرمجي من التأكّد من أنّ الملفات التي سيتم تعديلها تتضمّن المحتوى الأولي المتوقّع. في هذه المرحلة، لم يتم تغيير أي شيء على الجهاز. يمكنك أيضًا إصدار رمز لعمليات التحقّق الإضافية الخاصة بالجهاز.
IncrementalOTA_InstallBegin()
- يتم استدعاء هذه الطريقة بعد التأكّد من أنّ الملفات المطلوب تصحيحها تتضمّن الحالة السابقة المتوقّعة، ولكن قبل إجراء أي تغييرات. يمكنك إصدار أوامر لتحديثات خاصة بالجهاز يجب تنفيذها قبل إجراء أي تغييرات أخرى على الجهاز.
IncrementalOTA_InstallEnd()
- على غرار حزمة OTA الكاملة، يتم استدعاء هذا الإجراء في نهاية عملية إنشاء البرنامج النصي، بعد إصدار أوامر البرنامج النصي لتعديل أقسام التشغيل والنظام. يمكنك أيضًا إرسال أوامر إضافية لإجراء تحديثات خاصة بالجهاز.
ملاحظة: إذا انقطع التيار الكهربائي عن الجهاز، قد تتم إعادة تشغيل عملية تثبيت التحديث عبر الأثير من البداية. يجب الاستعداد للتعامل مع الأجهزة التي تم تنفيذ هذه الأوامر عليها بشكل كامل أو جزئي.
تمرير الدوال إلى كائنات المعلومات
مرِّر الدوال إلى عنصر معلومات واحد يحتوي على عناصر مفيدة مختلفة:
-
info.input_zip. (أنظمة OTA الكاملة فقط) عنصر
zipfile.ZipFile
لملف ZIP الخاص بملفات الإدخال المستهدَفة. -
info.source_zip. (تحديثات عبر الهواء التزايدية فقط) عنصر
zipfile.ZipFile
لملف ZIP الخاص بملفات المصدر المستهدَفة (الإصدار المثبَّت على الجهاز عند تثبيت الحزمة التزايدية). -
info.target_zip. (تحديثات عبر الهواء التزايدية فقط) عنصر
zipfile.ZipFile
لملف target-files .zip المستهدف (الإصدار الذي تضعه الحزمة التزايدية على الجهاز). -
info.output_zip. يتم إنشاء الحزمة، وتم فتح كائن
zipfile.ZipFile
للكتابة. استخدِم common.ZipWriteStr(info.output_zip, filename, data) لإضافة ملف إلى الحزمة. -
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 في ملف target-files.zip (META/releasetools.py
).
عند تشغيل أدوات الإصدار (إما img_from_target_files
أو ota_from_target_files
)، يُفضّل استخدام النص البرمجي releasetools.py في ملف target-files .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
.
آلية التثبيت من مصدر غير معروف
تتضمّن عملية الاسترداد آلية تحميل جانبي لتثبيت حزمة تحديث يدويًا بدون تنزيلها عبر الأثير من خلال النظام الرئيسي. تُعد عملية التثبيت من مصدر غير معروف مفيدة لتصحيح الأخطاء أو إجراء تغييرات على الأجهزة التي لا يمكن تشغيل النظام الرئيسي فيها.
في السابق، كان يتم التحميل الجانبي من خلال تحميل الحِزم من بطاقة 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
(أو أي شيء آخر غير حزمة صالحة) كحزمة، وسيتعذّر على الجهاز التحقّق منها وسيوقف عملية التثبيت. سيستمر استدعاء طريقةCheckKey()
في تنفيذ RecoveryUI عند الضغط على المفاتيح، لذا يمكنك توفير تسلسل مفاتيح يعيد تشغيل الجهاز ويعمل في وضع التحميل الجانبي عبر adb.