راهنمای سبک کد

سبک کد HIDL شبیه کد C++ در چارچوب Android است، با تورفتگی‌های ۴ فضایی و نام فایل‌های مختلط. اعلامیه‌های بسته، واردات، و رشته‌های مستندات مشابه موارد موجود در 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 یا OEM/ODM اشاره دارد.
  • 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 به فروشنده SoC یا OEM/ODM (نگاشت به 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 بسته فعلی (درصورت وجود) به‌طور خودکار وارد می‌شود (آن را به‌طور صریح وارد نکنید).

نام‌های کاملاً واجدشرایط (FQN)

فقط درصورت لزوم از نام‌های کاملاً واجدشرایط برای وارد کردن نوع تعریف‌شده توسط کاربر استفاده کنید. اگر نوع وارد کردن در همان بسته است، PACKAGE-NAME را حذف کنید. «نام کاملاً واجدشرایط» نباید فاصله داشته باشد. نمونه نام کاملاً واجدشرایط:

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;
}

نام‌ها را تایپ کنید

نام‌های نوع به تعریف‌های ساختار یا اتحاد، تعریف‌های نوع شمارشی، و typedefs اشاره دارند. برای این نام، از 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
};

توجه: نوع زیرین نوع شمارشی به‌طور صریح پس‌از دونقطه اعلام می‌شود. ازآنجایی‌که به گردآورنده وابسته نیست، استفاده از نوع واقعی شمارشی واضح‌تر است.

برای نام‌های کاملاً واجدشرایط برای مقادیر enum، از دونقطه بین نام نوع enum و نام مقدار enum استفاده می‌شود:

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 از // برای نظرات پشتیبانی می‌کند، اما استفاده از آن‌ها توصیه نمی‌شود زیرا در برونداد تولیدشده نشان داده نمی‌شوند.
  • از /** */ برای اسناد تولیدشده استفاده کنید. این‌ها را فقط می‌توان برای اعلان‌های نوع، روش، فیلد، و مقدار enum اعمال کرد. مثال:
    /** 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های اصلی، این باید پروانه 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 باید برای هر پارامتر اضافه شود. باید به‌دنبال نام پارامتر و سپس رشته مستند بیاید.
  • @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);

قوانین قالب‌بندی

قوانین قالب‌بندی عمومی شامل موارد زیر است:

  • طول خط. هر خط نوشتار باید حداکثر ۱۰۰ ستون طول داشته باشد.
  • فضاهای خالی. در خطوط فاصله سفید در انتها نباشد؛ خطوط خالی نباید حاوی فاصله سفید باشد.
  • فضاها دربرابر زبانه‌ها. فقط از فاصله استفاده کنید.
  • اندازه تورفتگی. برای بلوک‌ها از ۴ فاصله و برای شکستن خط از ۸ فاصله استفاده کنید
  • محکم کردن. به‌جز مقادیر گزارمان، آکلاد باز در همان خط کد قبلی قرار می‌گیرد، اما آکلاد بسته و نقطه‌ویرگول بعدی کل خط را اشغال می‌کنند. مثال:
    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 در همان خط پرانتز باز بعدی قرار دارد، با فاصله ادامه دهید.
  • همه پارامترها و مقادیر برگشتی را (درصورت امکان) تراز کنید.
  • تورفتگی پیش‌فرض ۴ فاصله است.
  • پارامترهای پیچیده با اولین پارامترهای خط قبلی تراز می‌شوند، درغیراین‌صورت، تورفتگی ۸ فاصله‌ای دارند.

حاشیه‌نویسی‌ها

از قالب زیر برای افزودن شرح استفاده کنید:

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

گزارمان‌ها را به‌ترتیب الفبا مرتب کنید و از فاصله در اطراف علامت‌های تساوی استفاده کنید. مثال:

@callflow(key = value)
@entry
@exit

مطمئن شوید که یک گزارمان کل خط را اشغال می‌کند. نمونه‌ها:

/* Good */
@entry
@exit

/* Bad */
@entry @exit

اگر گزارمان‌ها در همان خط جا نمی‌شوند، با ۸ فاصله تورفتگی ایجاد کنید. مثال:

@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 و پرانتز شکسته باز.
  • پرانتز شکسته باز و نوع عنصر (استثنا: نوع عنصر نیز a است 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;