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

سبک کد HIDL شبیه کد C++ در چارچوب اندروید است، با تورفتگی‌های ۴ فاصله‌ای و نام فایل‌ها با حروف بزرگ و کوچک. اعلان‌های بسته، importها و docstringها با اندکی تغییر مشابه جاوا هستند.

مثال‌های زیر برای 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
          • I INTERFACE_1 .hal
          • I INTERFACE_2 .hal
          • I INTERFACE_N .hal
          • types.hal (اختیاری)

کجا:

  • ROOT-DIRECTORY عبارت است از:
    • hardware/interfaces برای بسته‌های اصلی HIDL.
    • vendor/ VENDOR /interfaces برای بسته‌های فروشنده، که در آن VENDOR به یک فروشنده SoC یا یک OEM/ODM اشاره دارد.
  • MODULE باید یک کلمه با حروف کوچک باشد که زیرسیستم را توصیف می‌کند (برای مثال، nfc ). اگر به بیش از یک کلمه نیاز دارید، SUBMODULE تو در تو استفاده کنید. می‌تواند بیش از یک سطح تو در تو وجود داشته باشد.
  • VERSION باید دقیقاً همان نسخه (major.minor) باشد که در Versions توضیح داده شده است.
  • نام رابط باید I INTERFACE_X با UpperCamelCase / PascalCase (برای مثال، INfc ) باشد، همانطور که در بخش Interface names توضیح داده شده است.

مثال:

  • 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 نوشته شوند.
  • هیچ فاصله‌ای مجاز نیست.

FQN همیشه در اعلان‌های بسته استفاده می‌شود.

نسخه‌ها

نسخه‌ها باید فرمت زیر را داشته باشند:

MAJOR.MINOR

هر دو نسخه MAJOR و MINOR باید یک عدد صحیح واحد باشند. HIDL از قوانین نسخه‌بندی معنایی استفاده می‌کند.

واردات

یک ورودی می‌تواند یکی از سه فرمت زیر را داشته باشد:

  • واردات کل بسته: import PACKAGE-NAME ;
  • واردات جزئی: import PACKAGE-NAME :: UDT ; (یا اگر نوع وارد شده در همان بسته است، import UDT ;
  • ایمپورت‌های فقط-نوع: import PACKAGE-NAME ::types;

PACKAGE-NAME از فرمت موجود در Package names پیروی می‌کند. types.hal بسته فعلی (در صورت وجود) به طور خودکار وارد می‌شود (آن را صریحاً وارد نکنید).

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

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

android.hardware.nfc@1.0::INfcClientCallback

در فایل دیگری تحت android.hardware.nfc@1.0 ، به رابط فوق با عنوان INfcClientCallback اشاره کنید. در غیر این صورت، فقط از نام کامل آن استفاده کنید.

گروه‌بندی و مرتب‌سازی واردات

بعد از تعریف بسته (قبل از importها) از یک خط خالی استفاده کنید. هر import باید یک خط را اشغال کند و نباید تورفتگی داشته باشد. importها را به ترتیب زیر گروه‌بندی کنید:

  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 باشد (رابط I NAME باید در I NAME .hal باشد).

توابع

برای نام توابع، آرگومان‌ها و نام متغیرهای برگشتی، lowerCamelCase استفاده کنید. مثال:

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

نام‌های فیلدهای ساختار و اتحادیه

برای نام فیلدهای struct یا union، lowerCamelCase استفاده کنید. مثال:

struct FooReply {
    vec<uint8_t> replyData;
}

نام‌های تایپی

نام‌های نوع به تعاریف ساختار یا اتحادیه، تعاریف نوع شمارشی و typedef اشاره دارند. برای این نام‌ها، UpperCamelCase / PascalCase استفاده کنید. مثال‌ها:

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

مقادیر شمارشی

مقادیر enum باید UPPER_CASE_WITH_UNDERSCORES باشند. هنگام ارسال مقادیر enum به عنوان آرگومان‌های تابع و بازگرداندن آنها به عنوان خروجی تابع، از نوع enum واقعی استفاده کنید (نه نوع صحیح زیرین). مثال:

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

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

کامنت‌های TODO فقط در طول توسعه مجاز هستند؛ آن‌ها نباید در رابط‌های منتشر شده وجود داشته باشند.

توضیحات رابط و تابع (docstraints)

برای رشته‌های راهنمای چندخطی و تک‌خطی از /** */ استفاده کنید. برای رشته‌های راهنمای تک‌خطی از // استفاده نکنید.

رشته‌های مستندات رابط‌ها باید سازوکارهای کلی رابط، منطق طراحی، هدف و غیره را توصیف کنند. رشته‌های مستندات توابع باید مختص به آن تابع باشند (مستندات سطح بسته در یک فایل 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 اضافه شود. پس از آن باید نام پارامتر و سپس رشته‌ی مستندات (docstring) آن بیاید.
  • برای هر مقدار برگشتی باید @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);

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

قوانین کلی قالب‌بندی عبارتند از:

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

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

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

@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();

اعلان‌های شمارشی

برای تعریف enum از قوانین زیر استفاده کنید:

  • اگر تعریف‌های enum با بسته‌ی دیگری به اشتراک گذاشته شده‌اند، به جای جاسازی در یک رابط، تعریف‌ها را در types.hal قرار دهید.
  • قبل و بعد از علامت دو نقطه از یک فاصله استفاده کنید، و بعد از نوع زیرین قبل از آکولاد باز، از فاصله استفاده کنید.
  • آخرین مقدار enum ممکن است یک کاما اضافی نداشته باشد.

اعلان‌های ساختار

برای تعریف ساختارها از قوانین زیر استفاده کنید:

  • اگر اعلان‌های 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;