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

يشبه نمط رمز 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 هو الإصدار نفسه تمامًا (الرئيسي والثانوي) كما هو موضّح في الإصدارات.
  • يجب أن يكون 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 للحزمة الحالية (إذا كان متوفّرًا) (لا تستورده بشكل صريح).

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

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

android.hardware.nfc@1.0::INfcClientCallback

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

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

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

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

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

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 {
    /*...*/
};

قيم التعداد

يجب أن تكون قيم التعداد 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:
     * ...
     */

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

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

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

تعليقات TODO

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

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

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

تعليقات الواجهة والدوال (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 لكل قيمة معروضة. يجب أن يتبعها اسم القيمة المعروضة ثم سلسلة التوثيق.

مثال:

/**
 * 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;