دليل أنماط الرموز البرمجية

يشبه أسلوب رمز HIDL أسلوب رمز C++ في إطار عمل Android، مع 4 مسافات للترميز وأسماء ملفات بحالة مختلطة. تشبه نماذج بيانات الحِزم وعمليات الاستيراد وملفات نص وصف الدوالّ تلك المستخدَمة في Java، مع إجراء تعديلات طفيفة.

توضِّح الأمثلة التالية على IFoo.hal وtypes.hal أنماط رموز HIDL وتقدّم روابط سريعة للاطّلاع على تفاصيل كل أسلوب (تم حذف IFooClientCallback.hal وIBar.hal و IBaz.hal).

hardware/interfaces/foo/1.0/IFoo.hal
/*
 * (License Notice)
 */

package android.hardware.foo@1.0;

import android.hardware.bar@1.0::IBar;

import IBaz;
import IFooClientCallback;

/**
 * IFoo is an interface that*/
interface IFoo {

    /**
     * This is a multiline docstring.
     *
     * @return result 0 if successful, nonzero otherwise.
     */
     foo() generates (FooStatus result);

    /**
     * Restart controller by power cycle.
     *
     * @param bar callback interface that* @return result 0 if successful, nonzero otherwise.
     */
    powerCycle(IBar bar) generates (FooStatus result);

    /** Single line docstring. */
    baz();


    /**
     * The bar function.
     *
     * @param clientCallback callback after function is called
     * @param baz related baz object
     * @param data input data blob
     */
    bar(IFooClientCallback clientCallback,
        IBaz baz,
        FooData data);

};
hardware/interfaces/foo/1.0/types.hal
/*
 * (License Notice)
 */

package android.hardware.foo@1.0;

/** Replied status. */
enum Status : int32_t {
    OK,
    /* invalid arguments */
    ERR_ARG,
    /* note, no transport related errors */
    ERR_UNKNOWN = -1,
};

struct ArgData {
    int32_t[20]  someArray;
    vec<uint8_t> data;
};

اصطلاحات التسمية

يجب أن تكون أسماء الدوال وأسماء المتغيّرات وأسماء الملفات وصفية، ويجب تجنُّب استخدام اختصارات مفرطة. تعامل مع الاختصارات على أنّها كلمات (على سبيل المثال، استخدِم INfc بدلاً من INFC).

بنية الدليل وتسمية الملفات

من المفترض أن تظهر بنية الدليل على النحو التالي:

  • ROOT-DIRECTORY
    • MODULE
      • SUBMODULE (اختياري، يمكن أن يكون هناك أكثر من مستوى)
        • VERSION
          • Android.mk
          • IINTERFACE_1.hal
          • IINTERFACE_2.hal
          • IINTERFACE_N.hal
          • types.hal (اختياري)

المكان:

  • ROOT-DIRECTORY:
    • hardware/interfaces لحِزم HIDL الأساسية
    • vendor/VENDOR/interfaces لحِزم المورّدين، حيث يشير VENDOR إلى مورّد منظومة على الرقاقة أو المصنّع الأصلي للجهاز/المصنّع الأصلي للتصميم
  • يجب أن تكون MODULE كلمة واحدة صغيرة توضّح النظام الفرعي (على سبيل المثال، nfc). إذا كنت بحاجة إلى أكثر من كلمة واحدة، استخدِم SUBMODULE متداخلة. يمكن أن يكون هناك أكثر من مستوى واحد من التداخل.
  • يجب أن يكون VERSION هو الإصدار نفسه بالضبط (major.minor) كما هو موضّح في الإصدارات.
  • يجب أن يكون IINTERFACE_X هو اسم الواجهة مع UpperCamelCase/PascalCase (على سبيل المثال، INfc) كما هو موضّح في أسماء الواجهات.

مثال:

  • hardware/interfaces
    • nfc
      • 1.0
        • Android.mk
        • INfc.hal
        • INfcClientCallback.hal
        • types.hal

ملاحظة: يجب أن تحصل جميع الملفات على أذونات غير قابلة للتنفيذ (في Git).

أسماء الحِزم

يجب أن تستخدم أسماء الحِزم تنسيق الاسم المؤهَّل بالكامل (FQN) التالي (المُشار إليه باسم PACKAGE-NAME):

PACKAGE.MODULE[.SUBMODULE[.SUBMODULE[]]]@VERSION

المكان:

  • PACKAGE هي الحزمة التي يتم ربطها بالملف ROOT-DIRECTORY. على وجه التحديد، PACKAGE هو:
    • android.hardware لحِزم HIDL الأساسية (التعيين إلى hardware/interfaces).
    • vendor.VENDOR.hardware لحِزم المورّدين، حيث يشير VENDOR إلى مورّد منظومة على الرقاقة أو مورّد أصلي للأجهزة/مورّد تصميم أصلي للأجهزة (مع الربطvendor/VENDOR/interfaces)
  • MODULE[.SUBMODULE[.SUBMODULE[…]]]@VERSION هي أسماء المجلدات نفسها تمامًا في البنية الموضّحة في بنية الدليل.
  • يجب أن تكون أسماء الحِزم مكتوبة بأحرف صغيرة. إذا كانت الكلمات تزيد عن كلمة واحدة، يجب استخدامها كوحدات فرعية أو كتابتها باللغة snake_case.
  • لا يُسمح باستخدام أي مسافات.

يتم استخدام الاسم الكامل للفئة دائمًا في بيانات الحِزم.

الإصدارات

يجب أن تكون الإصدارات بالتنسيق التالي:

MAJOR.MINOR

يجب أن يكون كلّ من الإصدار MAJOR والإصدار MINOR عددًا تكامليًا واحدًا. يستخدم HIDL قواعد التسمية الدلالية.

الواردات

يكون لملف الاستيراد أحد التنسيقات الثلاثة التالية:

  • عمليات استيراد الحِزم بأكملها: import PACKAGE-NAME;
  • عمليات الاستيراد الجزئية: import PACKAGE-NAME::UDT; (أو إذا كان نوع المحتوى المستورَد مضمّنًا في الحزمة نفسها،import UDT;
  • عمليات الاستيراد التي تتضمّن الأنواع فقط: import PACKAGE-NAME::types;

يجب أن يتّبع PACKAGE-NAME التنسيق الموضّح في أسماء الحِزم. يتم تلقائيًا استيراد types.hal (إذا كان متوفّرًا) للحزمة الحالية (لا تستورده بشكل صريح).

الأسماء المؤهَّلة بالكامل

استخدِم الأسماء المؤهَّلة بالكامل لاستيراد نوع محدّد من قِبل المستخدم فقط عند الضرورة. وزِد PACKAGE-NAME إذا كان نوع الاستيراد في الحزمة نفسها. يجب ألا يحتوي الاسم الكامل للنظام على مسافات. مثال على اسم مؤهّل بالكامل:

android.hardware.nfc@1.0::INfcClientCallback

في ملف آخر ضمن android.hardware.nfc@1.0، يمكنك الإشارة إلى الواجهة أعلاه باسم INfcClientCallback. وبخلاف ذلك، استخدِم فقط الاسم المؤهَّل بالكامل.

تجميع عمليات الاستيراد وترتيبها

استخدِم سطرًا فارغًا بعد تعريف الحزمة (قبل عمليات الاستيراد). يجب أن يشغل كل استيراد سطرًا واحدًا وألا يكون مُسَبَّبًا. تجميع عمليات الاستيراد بالترتيب التالي:

  1. حِزم android.hardware الأخرى (استخدِم أسماء مؤهَّلة بالكامل).
  2. حِزم vendor.VENDOR الأخرى (استخدِم أسماء مؤهَّلة بالكامل).
    • يجب أن يكون كل مورّد مجموعة.
    • ترتيب المورّدين أبجديًا
  3. عمليات الاستيراد من واجهات أخرى في الحزمة نفسها (استخدِم أسماء بسيطة).

استخدِم سطرًا فارغًا بين المجموعات. داخل كل مجموعة، يمكنك ترتيب عمليات الاستيراد alfabetically. مثال:

import android.hardware.nfc@1.0::INfc;
import android.hardware.nfc@1.0::INfcClientCallback;

/* Importing the whole module. */
import vendor.barvendor.bar@3.1;

import vendor.foovendor.foo@2.2::IFooBar;
import vendor.foovendor.foo@2.2::IFooFoo;

import IBar;
import IFoo;

أسماء الواجهات

يجب أن تبدأ أسماء الواجهات برمز I، متبوعًا باسم UpperCamelCase/PascalCase. يجب تعريف واجهة باسم IFoo في الملف IFoo.hal. يمكن أن يحتوي هذا الملف على تعريفات لواجهة IFoo فقط (يجب أن تكون الواجهة INAME في INAME.hal).

الدوال

استخدِم lowerCamelCase لأسماء الدوال والوسيطات وأسماء متغيّرات الإرجاع. مثال:

open(INfcClientCallback clientCallback) generates (int32_t retVal);
oneway pingAlive(IFooCallback cb);

أسماء حقول البنية والاتحاد

استخدِم lowerCamelCase لأسماء حقول البنية أو الائتلاف. مثال:

struct FooReply {
    vec<uint8_t> replyData;
}

أسماء الأنواع

تشير أسماء الأنواع إلى تعريفات البنية أو البنية المشترَكة وتعريفات أنواع التعداد و typedef. بالنسبة إلى هذه الأسماء، استخدِم UpperCamelCase/PascalCase. على سبيل المثال:

enum NfcStatus : int32_t {
    /*...*/
};
struct NfcData {
    /*...*/
};

قيم التعداد

يجب أن تكون قيم enum هي UPPER_CASE_WITH_UNDERSCORES. عند تمرير قيم التعداد كوسائط للدالة وعرضها كنتائج للدالة، استخدِم نوع التعداد الفعلي (وليس النوع الأساسي للعدد الصحيح). مثال:

enum NfcStatus : int32_t {
    HAL_NFC_STATUS_OK               = 0,
    HAL_NFC_STATUS_FAILED           = 1,
    HAL_NFC_STATUS_ERR_TRANSPORT    = 2,
    HAL_NFC_STATUS_ERR_CMD_TIMEOUT  = 3,
    HAL_NFC_STATUS_REFUSED          = 4
};

ملاحظة: يتم تحديد النوع الأساسي لنوع التعداد بوضوح بعد النقطتَين. وبما أنّه لا يعتمد على المُجمِّع، يكون استخدام نوع التعداد الحقيقي أكثر وضوحًا.

بالنسبة إلى الأسماء المؤهَّلة بالكامل لقيم التعداد، يتم استخدام فاصلة بين اسم نوع التعداد واسم قيمة التعداد:

PACKAGE-NAME::UDT[.UDT[.UDT[…]]:ENUM_VALUE_NAME

يجب ألّا تكون هناك مسافات داخل اسم مؤهَّل بالكامل. استخدِم اسمًا مؤهَّلاً بالكامل فقط عند الضرورة واحذف الأجزاء غير الضرورية. مثال:

android.hardware.foo@1.0::IFoo.IFooInternal.FooEnum:ENUM_OK

التعليقات

بالنسبة إلى تعليق سطر واحد، يمكنك استخدام // و/* */ و/** */.

// This is a single line comment
/* This is also single line comment */
/** This is documentation comment */
  • استخدِم /* */ للتعليقات. على الرغم من أنّ HIDL تتيح استخدام // للتعليقات، لا يُنصح باستخدامها لأنّها لا تظهر في الإخراج الذي تم إنشاؤه.
  • استخدِم /** */ للمستندات التي تم إنشاؤها. يمكن تطبيقها فقط على تعريفات قيمة النوع والطريقة والحقل والعنصر. مثال:
    /** Replied status */
    enum TeleportStatus {
        /** Object entirely teleported. */
        OK              = 0,
        /** Methods return this if teleportation is not completed. */
        ERROR_TELEPORT  = 1,
        /**
         * Teleportation could not be completed due to an object
         * obstructing the path.
         */
        ERROR_OBJECT    = 2,
        ...
    }
  • ابدأ التعليقات المتعدّدة الأسطر برمز /** في سطر منفصل. استخدِم * في بداية كل سطر. اختَتم التعليق برمز */ في سطر منفصل مع محاذاة العلامات النجمية. مثال:
    /**
     * My multi-line
     * comment
     */
  • يجب أن يبدأ إشعار الترخيص وقوائم التغييرات بسطر جديد يتضمّن /* (نجمة واحدة)، ويجب استخدام * في بداية كل سطر، ويجب وضع */ في السطر الأخير بمفرده (يجب محاذاة العلامات النجمية). مثال:
    /*
     * Copyright (C) 2017 The Android Open Source Project
     * ...
     */
    
    /*
     * Changelog:
     * ...
     */

تعليقات الملفات

ابدأ كل ملف بإشعار الترخيص المناسب. بالنسبة إلى واجهات برمجة التطبيقات الأساسية لنظام التشغيل HAL، يجب أن يكون هذا الرمز هو ترخيص Apache AOSP في ملف development/docs/copyright-templates/c.txt. احرص على تعديل السنة واستخدام تعليقات متعددة الأسطر بأسلوب /* */ كما هو موضّح أعلاه.

يمكنك اختياريًا وضع سطر فارغ بعد إشعار الترخيص، ثم يليه معلومات حول سجلّ التغييرات أو معلومات حول الإصدار. استخدِم /* */ أسلوبًا لتعليقات متعددة الأسطر كما هو موضّح أعلاه، واضبط السطر الفارغ بعد ملف ملف changelog، ثمّ اتّبِع ببيان الحزمة.

تعليقات TODO

يجب أن تتضمّن المهام التي يجب إكمالها السلسلة TODO بأحرف كبيرة متبوعة بعلامة colon ( colon). مثال:

// TODO: remove this code before foo is checked in.

لا يُسمح بتعليقات TODO إلا أثناء مرحلة التطوير، ويجب عدم تضمينها في الواجهات المنشورة.

تعليقات الواجهة والدوالّ (مستندات نص الدوالّ)

استخدِم /** */ لجداول وصف متعددة الأسطر وتلك التي تتألف من سطر واحد. لا تستخدِم // لجداول وصف الدوال البرمجية.

يجب أن تصف سلاسل Docstrings للواجهات الآليات العامة للواجهة، وسبب التصميم، والغرض، وما إلى ذلك. ويجب أن تكون سلاسل Docstrings للدوالّ مرتبطة بالدالة (يتم وضع التوثيق على مستوى الحزمة في ملف README في دليل الحزمة).

/**
 * IFooController is the controller for foos.
 */
interface IFooController {
    /**
     * Opens the controller.
     *
     * @return status HAL_FOO_OK if successful.
     */
    open() generates (FooStatus status);

    /** Close the controller. */
    close();
};

يجب إضافة @param و@return لكل مَعلمة/قيمة معروضة:

  • يجب إضافة @param لكل مَعلمة. يجب أن يتبعه اسم المَعلمة ثم الوصف النصي للدالة.
  • يجب إضافة @return لكل قيمة معروضة. يجب أن يليه اسم القيمة المعروضة ثم الdocstring.

مثال:

/**
 * Explain what foo does.
 *
 * @param arg1 explain what arg1 is
 * @param arg2 explain what arg2 is
 * @return ret1 explain what ret1 is
 * @return ret2 explain what ret2 is
 */
foo(T arg1, T arg2) generates (S ret1, S ret2);

قواعد التنسيق

تشمل قواعد التنسيق العامة ما يلي:

  • طول السطر: يجب ألا يزيد طول كل سطر من النص عن 100 عمود.
  • المسافات البيضاء يجب عدم تضمين مسافات بيضاء في نهاية الأسطر، ويجب ألا تحتوي الأسطر الفارغة على مسافات بيضاء.
  • المساحات مقارنةً بعلامات التبويب استخدِم المسافات فقط.
  • حجم المسافة البادئة: استخدِم 4 مسافات للكتلة و 8 مسافات للف السطر.
  • التثبيت: باستثناء قيم التعليق التوضيحي، يتم وضع قوس مفتوح في السطر نفسه الذي يتضمّن الرمز السابق، ولكن يشغل قوس مغلق والفاصلة المنقوطة التالية السطر بأكمله. مثال:
    interface INfc {
        close();
    };

بيان الحزمة

يجب أن يكون بيان الحزمة في أعلى الملف بعد إشعار الترخيص ، ويجب أن يشغل السطر بأكمله، وألا يكون مُسطرًا. يتم تحديد الحِزم باستخدام التنسيق التالي (لتنسيق الاسم، يُرجى الاطّلاع على أسماء الحِزم):

package PACKAGE-NAME;

مثال:

package android.hardware.nfc@1.0;

تعريفات الدوالّ

يجب أن يكون اسم الدالة والمَعلمات وgenerates والقيم المعروضة في السطر نفسه إذا كان ذلك مناسبًا. مثال:

interface IFoo {
    /** ... */
    easyMethod(int32_t data) generates (int32_t result);
};

إذا لم تتسع المَعلمات وقيم الإرجاع في السطر نفسه، حاوِل وضع المَعلمات وقيم الإرجاع في مستوى المسافة البادئة نفسه مع التمييز بينهما generate لمساعدة القارئ على الاطّلاع بسرعة على المَعلمات وقيم الإرجاع. مثال:

interface IFoo {
    suchALongMethodThatCannotFitInOneLine(int32_t theFirstVeryLongParameter,
                                          int32_t anotherVeryLongParameter);
    anEvenLongerMethodThatCannotFitInOneLine(int32_t theFirstLongParameter,
                                             int32_t anotherVeryLongParameter)
                                  generates (int32_t theFirstReturnValue,
                                             int32_t anotherReturnValue);
    superSuperSuperSuperSuperSuperSuperLongMethodThatYouWillHateToType(
            int32_t theFirstVeryLongParameter, // 8 spaces
            int32_t anotherVeryLongParameter
        ) generates (
            int32_t theFirstReturnValue,
            int32_t anotherReturnValue
        );
    /* method name is even shorter than 'generates' */
    foobar(AReallyReallyLongType aReallyReallyLongParameter,
           AReallyReallyLongType anotherReallyReallyLongParameter)
        generates (ASuperLongType aSuperLongReturnValue, // 4 spaces
                   ASuperLongType anotherSuperLongReturnValue);
}

تفاصيل إضافية:

  • يظهر القوس المفتوح دائمًا في السطر نفسه الذي يظهر فيه اسم الدالة.
  • لا تترك مسافات بين اسم الدالة والقوس المفتوح.
  • لا تُدرِج مسافات بين الأقواس والمَعلمات إلا عند استخدام علامات تغذية الأسطر بينهما.
  • إذا كان الرمز generates في السطر نفسه الذي يتضمّن المربّع المغلق السابق، استخدِم مسافة قبله. إذا كان الرمز generates في السطر نفسه مع القوس المفتوح التالي، أضِف مسافة بعده.
  • محاذاة جميع المَعلمات وعرض القيم (إن أمكن)
  • المسافة البادئة التلقائية هي 4 مسافات.
  • تتمّ محاذاة المَعلمات المُدرَجة مع المَعلمات الأولى في السطر السابق، وإلا يتمّ وضع مسافة بادئة تبلغ 8 مسافات.

التعليقات التوضيحية

استخدِم التنسيق التالي للتعليقات التوضيحية:

@annotate(keyword = value, keyword = {value, value, value})

رتِّب التعليقات التوضيحية أبجديًا، واستخدِم مسافات حول علامات المساواة. مثال:

@callflow(key = value)
@entry
@exit

تأكَّد من أنّ التعليق التوضيحي يشغل السطر بأكمله. أمثلة:

/* Good */
@entry
@exit

/* Bad */
@entry @exit

إذا تعذّر وضع التعليقات التوضيحية في السطر نفسه، اضبط مسافة بادئة بـ 8 مسافات. مثال:

@annotate(
        keyword = value,
        keyword = {
                value,
                value
        },
        keyword = value)

إذا لم يكن بالإمكان عرض صفيف القيم بالكامل في السطر نفسه، ضَع فواصل أسطر بعد الشَرطَين المفتوحة{ وبعد كل فاصلة داخل الصفيف. ضَع قوس إغلاق بعد القيمة الأخيرة مباشرةً. لا تضِف الأقواس إذا كانت هناك قيمة واحدة فقط.

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

/* Good */
@callflow(key = {"val", "val"})

/* Bad */
@callflow(key = { "val","val" })

يجب ألّا تكون هناك أسطر فارغة بين التعليقات التوضيحية وبيان الدالة. أمثلة:

/* Good */
@entry
foo();

/* Bad */
@entry

foo();

تعريفات التعداد

استخدِم القواعد التالية لإعلانات التعداد:

  • في حال مشاركة بيانات التعداد مع حزمة أخرى، ضَع البيانات في types.hal بدلاً من تضمينها داخل واجهة.
  • استخدِم مسافة قبل النقطتَين وبعدهما، ومسافة بعد النوع الأساسي قبل القوس المفتوح.
  • قد لا تحتوي قيمة القائمة المُدرَجة الأخيرة على فاصلة إضافية.

تعريفات البنية

استخدِم القواعد التالية لتعريفات البنية:

  • في حال مشاركة بيانات تعريف البنية مع حزمة أخرى، ضَع بيانات التعريف في types.hal بدلاً من تضمينها داخل واجهة.
  • استخدِم مسافة بعد اسم نوع البنية قبل القوس المفتوح.
  • محاذاة أسماء الحقول (اختياري) مثال:
    struct MyStruct {
        vec<uint8_t>   data;
        int32_t        someInt;
    }

تعريفات الصفائف

لا تضِف مسافات بين ما يلي:

  • نوع العنصر وقوس مربّع مفتوح
  • افتح قوسًا مربّعًا وحجم الصفيف.
  • حجم الصفيف والقوس المربّع للإغلاق
  • قوس مربع مغلق وقوس مربع مفتوح تالٍ له، إذا كان هناك أكثر من سمة واحدة

أمثلة:

/* Good */
int32_t[5] array;

/* Good */
int32_t[5][6] multiDimArray;

/* Bad */
int32_t [ 5 ] [ 6 ] array;

المتجهات

لا تضِف مسافات بين ما يلي:

  • vec وقوس زاوية للفتح
  • قوس زاوية للفتح ونوع العنصر (استثناء: نوع العنصر هو أيضًا vec).
  • نوع العنصر وقوس زاوية الإغلاق (استثناء: نوع العنصر هو أيضًا vec)

أمثلة:

/* Good */
vec<int32_t> array;

/* Good */
vec<vec<int32_t>> array;

/* Good */
vec< vec<int32_t> > array;

/* Bad */
vec < int32_t > array;

/* Bad */
vec < vec < int32_t > > array;