دليل نمط الشفرة

يتشابه نمط رمز 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 إلى مورّد منظومة على الرقاقة (SoC) أو المصنّع الأصلي للجهاز/المصنّع الأصلي للجهاز:
  • يجب أن يكون 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 إلى مورّد منظومة على الرقاقة (SoC) أو مصنّع أصلي للجهاز أو مصنّع أصلي للجهاز (ربط البيانات). على vendor/VENDOR/interfaces).
  • MODULE[.SUBMODULE[.SUBMODULE[…]]]@VERSION هي نفس أسماء المجلدات في البنية الموضحة في بنية الدليل:
  • يجب أن تكون أسماء الحِزم بأحرف صغيرة. إذا كانت أكثر من كلمة واحدة، يجب استخدامها كوحدات فرعية أو مكتوبة بالترميز snake_case.
  • لا يُسمح باستخدام أي مساحات.

يتم استخدام FQN دائمًا في إعلانات الحزمة.

الإصدارات

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

MAJOR.MINOR

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

الواردات

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

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

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

الأسماء المؤهلة بالكامل (FQN)

استخدم أسماء مؤهلة بالكامل لاستيراد نوع من تحديد المستخدم عند الضرورة فقط. حذف 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:
     * ...
     */
    

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

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

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

تعليقات TODO

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

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

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

تعليقات الواجهة والوظائف (سلاسل المستندات)

استخدِم /** */ للسلاسل من المستندات المتعددة الأسطر والسطر واحد. عدم الاستخدام // لسلاسل المستندات.

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

إعلانات Enum

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

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

طلبات بيانات البنية

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

  • إذا تمت مشاركة تصريحات struct مع حزمة أخرى، ضع تعريفات في 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;