رمز خاص بالجهاز

يتضمن نظام الاسترداد عدة خطافات لإدخال رمز خاص بالجهاز بحيث يمكن لتحديثات OTA أيضًا تحديث أجزاء من الجهاز بخلاف نظام Android (على سبيل المثال، النطاق الأساسي أو معالج الراديو).

تقوم الأقسام والأمثلة التالية بتخصيص جهاز tardis الذي ينتجه بائع 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 .
تحويلة4
نظام ملفات ext4 أعلى جهاز فلاش eMMc. يجب أن يكون "الجهاز" هو مسار جهاز الكتلة.
emmc
جهاز كتلة eMMc خام، يستخدم للأقسام القابلة للتمهيد مثل التمهيد والاسترداد. كما هو الحال مع نوع mtd، لا يتم تثبيت eMMc فعليًا أبدًا، ولكن يتم استخدام سلسلة نقطة التثبيت لتحديد موقع الجهاز في الجدول.
vfat
نظام ملفات FAT فوق جهاز كتلة، عادةً للتخزين الخارجي مثل بطاقة SD. الجهاز هو جهاز الكتلة؛ جهاز 2 هو جهاز كتلة ثانٍ يحاول النظام تثبيته في حالة فشل تثبيت الجهاز الأساسي (للتوافق مع بطاقات SD التي قد تكون أو لا تكون مهيأة بجدول أقسام).

يجب تثبيت كافة الأقسام في الدليل الجذر (أي يجب أن تبدأ قيمة نقطة التثبيت بشرطة مائلة ولا تحتوي على خطوط مائلة أخرى). ينطبق هذا القيد فقط على أنظمة الملفات المتصاعدة في الاسترداد؛ النظام الرئيسي مجاني لتركيبها في أي مكان. يجب أن تكون الدلائل /boot و /recovery و /misc من الأنواع الأولية (mtd أو emmc)، في حين أن الدلائل /system و /data و /cache و /sdcard (إن وجدت) يجب أن تكون من أنواع أنظمة الملفات (yaffs2 أو ext4 أو ففات).

بدءًا من Android 3.0، يكتسب ملف Recovery.fstab حقلًا اختياريًا إضافيًا، وهو options . الخيار الوحيد المحدد حاليًا هو length ، والذي يتيح لك تحديد طول القسم بشكل واضح. يُستخدم هذا الطول عند إعادة تهيئة القسم (على سبيل المثال، لقسم بيانات المستخدم أثناء عملية مسح البيانات/إعادة ضبط المصنع، أو لقسم النظام أثناء تثبيت حزمة OTA كاملة). إذا كانت قيمة الطول سالبة، فسيتم أخذ الحجم المطلوب تنسيقه عن طريق إضافة قيمة الطول إلى حجم القسم الحقيقي. على سبيل المثال، يعني تعيين "الطول=-16384" أن آخر 16 كيلو بايت من هذا القسم لن تتم الكتابة فوقها عند إعادة تهيئة هذا القسم. يدعم هذا ميزات مثل تشفير قسم بيانات المستخدم (حيث يتم تخزين بيانات تعريف التشفير في نهاية القسم الذي لا ينبغي الكتابة فوقه).

ملحوظة: حقلي الجهاز 2 والخيارات اختياريان، مما يخلق غموضًا في التحليل. إذا كان الإدخال في الحقل الرابع على السطر يبدأ بحرف '/'، فإنه يعتبر إدخالاً لجهاز 2 ؛ إذا لم يبدأ الإدخال بحرف '/'، فإنه يعتبر حقل خيارات .

تمهيد الرسوم المتحركة

تتمتع الشركات المصنعة للأجهزة بالقدرة على تخصيص الرسوم المتحركة التي تظهر عند تشغيل جهاز Android. للقيام بذلك، قم بإنشاء ملف .zip منظم ومحدد وفقًا للمواصفات الموجودة في تنسيق bootanimation .

بالنسبة لأجهزة Android Things ، يمكنك تحميل الملف المضغوط في وحدة تحكم Android Things لتضمين الصور في المنتج المحدد.

ملحوظة: يجب أن تتوافق هذه الصور مع إرشادات العلامة التجارية لنظام 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 };

ملحوظة: الخطوط الطويلة مقطوعة (غير مغلفة)، لذا ضع في الاعتبار عرض شاشة جهازك.

تخصيص 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() بغض النظر عما يحدث في بقية عملية الاسترداد: عند إيقاف تشغيل القائمة، أو عند تشغيلها، أو أثناء تثبيت الحزمة، أو أثناء مسح بيانات المستخدم، وما إلى ذلك. ويمكنها إرجاع أحد الثوابت الأربعة:

  • تبديل . تبديل عرض القائمة و/أو تسجيل الدخول أو إيقاف تشغيله
  • اعادة التشغيل . إعادة تشغيل الجهاز على الفور
  • يتجاهل . تجاهل هذه الضغطة على المفاتيح
  • ENQUUE . قم بإدراج ضغطة المفتاح هذه ليتم استهلاكها بشكل متزامن (على سبيل المثال، من خلال نظام قائمة الاسترداد إذا تم تمكين العرض)

يتم استدعاء 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 للتحكم في سرعة الإطارات في الثانية (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() في بداية الاسترداد، بعد تهيئة واجهة المستخدم وبعد تحليل الوسائط، ولكن قبل اتخاذ أي إجراء. التنفيذ الافتراضي لا يفعل شيئًا، لذلك لا تحتاج إلى توفير هذا في فئتك الفرعية إذا لم يكن لديك ما تفعله:

   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() أدناه). وإلا فإنه يمكن أن يكون أحد الثوابت المحددة مسبقًا التالية:

  • ك هايلايت اب . نقل تمييز القائمة إلى العنصر السابق
  • ك هايلايت داون . انقل تمييز القائمة إلى العنصر التالي
  • kInvocItem . استدعاء العنصر المميز حاليًا
  • 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
        }
        ...
    }

ملاحظة: إذا كانت القيمة "مرئي" خاطئة، فليس من المنطقي إرجاع القيم الخاصة التي تتعامل مع القائمة (نقل التمييز، واستدعاء العنصر المميز) نظرًا لأن المستخدم لا يمكنه رؤية التمييز. ومع ذلك، يمكنك إرجاع القيم إذا رغبت في ذلك.

InvocationMenuItem

بعد ذلك، قم بتوفير أسلوب 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;
        }
    }

يمكن لهذه الطريقة إرجاع أي عضو في تعداد BuildinAction لإخبار النظام باتخاذ هذا الإجراء (أو عضو NO_ACTION إذا كنت تريد ألا يفعل النظام شيئًا). هذا هو المكان المناسب لتوفير وظائف استرداد إضافية تتجاوز ما هو موجود في النظام: أضف عنصرًا له في قائمتك، وقم بتنفيذه هنا عند استدعاء عنصر القائمة هذا، ثم قم بإرجاع NO_ACTION حتى لا يفعل النظام أي شيء آخر.

يحتوي برنامج BuildinAction على القيم التالية:

  • لا رد فعل . لا تفعل شيئا.
  • اعادة التشغيل . اخرج من الريكفري وأعد تشغيل الجهاز بشكل طبيعي.
  • APPLY_EXT، APPLY_CACHE، APPLY_ADB_SIDELOAD . قم بتثبيت حزمة التحديث من أماكن مختلفة. لمزيد من التفاصيل، راجع التحميل الجانبي .
  • مسح ذاكرة التخزين المؤقت . إعادة تهيئة قسم ذاكرة التخزين المؤقت فقط. لا يوجد تأكيد مطلوب لأن هذا غير ضار نسبيًا.
  • بيانات جاهزة . أعد تهيئة أقسام بيانات المستخدم وذاكرة التخزين المؤقت، والمعروفة أيضًا باسم إعادة ضبط بيانات المصنع. يُطلب من المستخدم تأكيد هذا الإجراء قبل المتابعة.

الطريقة الأخيرة، 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

صور واجهة المستخدم الاسترداد

تتكون واجهة مستخدم الاسترداد من الصور. من الناحية المثالية، لا يتفاعل المستخدمون أبدًا مع واجهة المستخدم: أثناء التحديث العادي، يقوم الهاتف بالتمهيد إلى الاسترداد، ويملأ شريط تقدم التثبيت، ثم يعود إلى النظام الجديد دون إدخال من المستخدم. في حالة حدوث مشكلة في تحديث النظام، فإن الإجراء الوحيد الذي يمكن اتخاذه للمستخدم هو الاتصال بخدمة العملاء.

واجهة الصورة فقط تغني عن الحاجة إلى الترجمة. ومع ذلك، اعتبارًا من Android 5.0، يمكن للتحديث عرض سلسلة نصية (على سبيل المثال، "تثبيت تحديث النظام...") مع الصورة. للحصول على التفاصيل، راجع نص الاسترداد المترجم .

أندرويد 5.0 والإصدارات الأحدث

تستخدم واجهة مستخدم الاسترداد لنظام التشغيل Android 5.0 والإصدارات الأحدث صورتين رئيسيتين: صورة الخطأ والرسوم المتحركة للتثبيت .

تظهر الصورة أثناء خطأ OTA

الشكل 1. icon_error.png

تظهر الصورة أثناء تثبيت OTA

الشكل 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 4.x والإصدارات السابقة لاسترداد البيانات صورة الخطأ (الموضحة أعلاه) والرسوم المتحركة للتثبيت بالإضافة إلى عدة صور متراكبة:

تظهر الصورة أثناء تثبيت OTA

الشكل 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 (رمز الخطأ ليس متحركًا؛ فهو دائمًا صورة ثابتة).

نص الاسترداد المترجم

يعرض Android 5.x سلسلة نصية (على سبيل المثال، "تثبيت تحديث النظام...") بالإضافة إلى الصورة. عندما يقوم النظام الرئيسي بالتمهيد إلى الاسترداد، فإنه يمرر الإعدادات المحلية الحالية للمستخدم كخيار سطر أوامر للاسترداد. بالنسبة لكل رسالة يتم عرضها، يتضمن الاسترداد صورة مركبة ثانية مع سلاسل نصية معروضة مسبقًا لتلك الرسالة في كل لغة.

صورة نموذجية لسلاسل نصية للاسترداد:

صورة نص الاسترداد

الشكل 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

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

شريط التقدم عند 1%

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

شريط التقدم عند 10%

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

شريط التقدم بنسبة 50%

الشكل 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() عليها في النهاية.

لنفترض أن الوظيفة تحتاج إلى وسيطتين: مفتاح ذو قيمة سلسلة وصورة ذات قيمة كبيرة. يمكنك قراءة الحجج مثل هذا:

   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'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" : ""));
}

لربط الوظائف بمترجم التحرير، قم بتوفير الوظيفة Register_ foo حيث foo هو اسم المكتبة الثابتة التي تحتوي على هذا الرمز. قم باستدعاء RegisterFunction() لتسجيل كل وظيفة ملحقة. وفقًا للاتفاقية، قم بتسمية الوظائف الخاصة device . whatever لتجنب التعارض مع الوظائف المدمجة المستقبلية المضافة.

void Register_librecovery_updater_tardis() {
    RegisterFunction("tardis.reprogram", ReprogramTardisFn);
}

يمكنك الآن تكوين ملف makefile لإنشاء مكتبة ثابتة باستخدام التعليمات البرمجية الخاصة بك. (هذا هو نفس ملف التكوين المستخدم لتخصيص واجهة مستخدم الاسترداد في القسم السابق؛ قد يحتوي جهازك على المكتبتين الثابتتين المحددتين هنا.)

device/yoyodyne/tardis/recovery/Android.mk
include $(CLEAR_VARS)
LOCAL_SRC_FILES := recovery_updater.c
LOCAL_C_INCLUDES += bootable/recovery

يجب أن يتطابق اسم المكتبة الثابتة مع اسم وظيفة Register_ libname الموجودة بداخلها.

LOCAL_MODULE := librecovery_updater_tardis
include $(BUILD_STATIC_LIBRARY)

وأخيرًا، قم بتكوين بنية الاسترداد لسحب مكتبتك. أضف مكتبتك إلى TARGET_RECOVERY_UPDATER_LIBS (والتي قد تحتوي على مكتبات متعددة؛ سيتم تسجيلها جميعًا). إذا كان الكود الخاص بك يعتمد على مكتبات ثابتة أخرى ليست بحد ذاتها امتدادات edify (على سبيل المثال، لا تحتوي على وظيفة Register_ libname )، فيمكنك إدراج تلك الموجودة في TARGET_RECOVERY_UPDATER_EXTRA_LIBS لربطها بالمُحدِّث دون استدعاء وظيفة التسجيل (غير الموجودة) الخاصة بها. على سبيل المثال، إذا أراد الكود الخاص بجهازك استخدام zlib لفك ضغط البيانات، فيمكنك تضمين libz هنا.

device/yoyodyne/tardis/BoardConfig.mk
 [...]

# add device-specific extensions to the updater binary
TARGET_RECOVERY_UPDATER_LIBS += librecovery_updater_tardis
TARGET_RECOVERY_UPDATER_EXTRA_LIBS +=

يمكن الآن لبرامج التحديث النصية الموجودة في حزمة OTA الخاصة بك استدعاء وظيفتك كأي وظيفة أخرى. لإعادة برمجة جهاز tardis، قد يحتوي البرنامج النصي للتحديث على: tardis.reprogram("the-key", package_extract_file("tardis-image.dat")) . يستخدم هذا إصدار الوسيطة الفردية للوظيفة المضمنة package_extract_file() ، والتي تُرجع محتويات الملف المستخرج من حزمة التحديث ككائن ثنائي كبير الحجم لإنتاج الوسيطة الثانية لوظيفة الامتداد الجديدة.

توليد حزمة OTA

المكون الأخير هو الحصول على أدوات إنشاء حزمة OTA للتعرف على البيانات الخاصة بجهازك وإصدار نصوص برمجية للتحديث تتضمن استدعاءات لوظائف الامتداد الخاصة بك.

أولاً، اجعل نظام الإنشاء يتعرف على مجموعة كبيرة من البيانات الخاصة بالجهاز. بافتراض أن ملف البيانات الخاص بك موجود في device/yoyodyne/tardis/tardis.dat ، قم بتعريف ما يلي في AndroidBoard.mk بجهازك:

device/yoyodyne/tardis/AndroidBoard.mk
  [...]

$(call add-radio-file,tardis.dat)

يمكنك أيضًا وضعه في Android.mk بدلاً من ذلك، ولكن يجب حمايته عن طريق فحص الجهاز، حيث يتم تحميل جميع ملفات Android.mk الموجودة في الشجرة بغض النظر عن الجهاز الذي يتم إنشاؤه. (إذا كانت شجرتك تتضمن أجهزة متعددة، فأنت تريد فقط إضافة ملف tardis.dat عند إنشاء جهاز tardis.)

device/yoyodyne/tardis/Android.mk
  [...]

# an alternative to specifying it in AndroidBoard.mk
ifeq (($TARGET_DEVICE),tardis)
  $(call add-radio-file,tardis.dat)
endif

تُسمى هذه الملفات بملفات الراديو لأسباب تاريخية؛ قد لا يكون لها أي علاقة براديو الجهاز (إن وجد). إنها ببساطة عبارة عن نقاط غير شفافة من البيانات التي ينسخها نظام الإنشاء إلى الملفات المستهدفة .zip التي تستخدمها أدوات إنشاء OTA. عندما تقوم بالإنشاء، يتم تخزين tardis.dat في الملف 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، يتم استدعاء هذا في نهاية إنشاء البرنامج النصي، بعد إصدار أوامر البرنامج النصي لتحديث أقسام التمهيد والنظام. يمكنك أيضًا إصدار أوامر إضافية للتحديثات الخاصة بالجهاز.

ملاحظة: إذا انقطع الجهاز عن الطاقة، فقد تتم إعادة تشغيل التثبيت عبر الهواء من البداية. كن مستعدًا للتعامل مع الأجهزة التي تم بالفعل تشغيل هذه الأوامر عليها، كليًا أو جزئيًا.

تمرير الوظائف إلى كائنات المعلومات

قم بتمرير الوظائف إلى كائن معلومات واحد يحتوي على العديد من العناصر المفيدة:

  • info.input_zip . (وكالات السفر عبر الإنترنت الكاملة فقط) كائن zipfile.ZipFile لملفات هدف الإدخال .zip.
  • info.source_zip . (وكالات السفر عبر الإنترنت التزايدية فقط) كائن zipfile.ZipFile لملفات الهدف المصدر .zip (الإصدار الموجود بالفعل على الجهاز عند تثبيت الحزمة التزايدية).
  • info.target_zip . (وكالات السفر عبر الإنترنت التزايدية فقط) كائن zipfile.ZipFile للملفات المستهدفة المستهدفة .zip (البنية التي تضعها الحزمة التزايدية على الجهاز).
  • info.output_zip . الحزمة قيد الإنشاء؛ تم فتح كائن zipfile.ZipFile للكتابة. استخدم common.ZipWriteStr(info.output_zip, filename , data ) لإضافة ملف إلى الحزمة.
  • معلومات. البرنامج النصي . كائن البرنامج النصي الذي يمكنك إلحاق الأوامر به. اتصل بـ info.script.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() بجهازك إرجاع القيم التالية لـ BuildinginAction:

  • APPLY_EXT . قم بتحميل حزمة التحديث من وحدة التخزين الخارجية (دليل /sdcard ). يجب أن يحدد ملف Recovery.fstab الخاص بك نقطة تحميل /sdcard . هذا ليس قابلاً للاستخدام على الأجهزة التي تحاكي بطاقة SD مع ارتباط مع /data (أو بعض آلية مماثلة). /data غير متوفرة للاسترداد لأنه قد يتم تشفيرها. يعرض UI Recovery قائمة من ملفات .zip في /sdcard ويسمح للمستخدم بتحديد واحدة.
  • application_cache . على غرار تحميل حزمة من /sdcard باستثناء أن دليل /cache (والذي يتوفر دائمًا للاسترداد) يستخدم بدلاً من ذلك. من النظام العادي ، لا يمكن كتابة /cache إلا من قبل المستخدمين المتميزين ، وإذا لم يكن الجهاز قابل للتمهيد ، فلا يمكن كتابة دليل /cache على الإطلاق (مما يجعل هذه الآلية ذات الأداة المساعدة المحدودة).
  • application_adb_sideload . يسمح للمستخدم بإرسال حزمة إلى الجهاز عبر كبل USB وأداة تطوير ADB. عندما يتم استدعاء هذه الآلية ، تبدأ الاسترداد في النسخة الصغيرة الخاصة بها من Daemon ADBD للسماح لـ ADB على جهاز كمبيوتر مضيف متصل يتحدث إليه. يدعم هذه النسخة المصغرة أمرًا واحدًا فقط: adb sideload filename . يتم إرسال الملف المسماة من الجهاز المضيف إلى الجهاز ، والذي يتحقق بعد ذلك ويقوم بتثبيته كما لو كان على التخزين المحلي.

بعض التحذيرات:

  • يتم دعم نقل USB فقط.
  • إذا تم تشغيل استردادك بشكل طبيعي (عادةً ما يكون الأمر صحيحًا بالنسبة لـ userDebug و Eng) ، فسيتم إيقاف تشغيله أثناء وجود الجهاز في وضع ADB SideLoad وسيتم إعادة تشغيله عندما ينتهي ADB SideLoad من تلقي الحزمة. أثناء وجوده في وضع ADB SideLoad ، لا توجد أوامر ADB بخلاف العمل sideload ( logcat ، reboot ، push ، pull ، shell ، إلخ. كلها تفشل).
  • لا يمكنك الخروج من وضع التحميل الجانبي ADB على الجهاز. للإجهاض ، يمكنك إرسال /dev/null (أو أي شيء آخر ليس حزمة صالحة) كحزمة ، وبعد ذلك سيفشل الجهاز في التحقق منه وإيقاف إجراء التثبيت. سيستمر استمرار استدعاء طريقة CheckKey() لتنفيذ Recoveryui ، بحيث يمكنك توفير تسلسل مفتاح يعيد تشغيل الجهاز ويعمل في وضع ADB SideLoad.