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

سبک کد HIDL شبیه کد ++C در چارچوب اندروید است که دارای تورفتگی‌های ۴ فضایی و نام فایل‌های مختلط است. اعلان‌های بسته، واردات و رشته‌های اسناد مشابه موارد موجود در جاوا هستند، با تغییرات جزئی.

مثال‌های زیر برای 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) باشد که در نسخه ها توضیح داده شده است.
  • I INTERFACE_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 دقیقاً همان نام پوشه ها در ساختار توضیح داده شده در ساختار Directory هستند.
  • نام بسته ها باید با حروف کوچک باشد. اگر بیش از یک کلمه باشد، کلمات باید یا به عنوان زیر ماژول استفاده شوند یا در 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 بسته های 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);

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

برای نام‌های فیلد ساختار یا اتحاد، از lowerCamelCase استفاده کنید. مثال:

struct FooReply {
    vec<uint8_t> replyData;
}

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

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

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

مقادیر Enum

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

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

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

نظرات رابط و عملکرد (رشته اسناد)

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

رشته‌های اسناد برای رابط‌ها باید مکانیسم‌های کلی رابط، منطق طراحی، هدف، و غیره را توصیف کنند.

/**
 * 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();
};

برای هر پارامتر/مقدار بازگشتی باید s @param و s @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);

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

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

  • طول خط . هر خط متن باید حداکثر 100 ستون داشته باشد.
  • فضاهای خالی بدون فاصله خالی در خطوط. خطوط خالی نباید دارای فضای خالی باشند.
  • Spaces در مقابل برگه ها فقط از فضاها استفاده کنید
  • اندازه تورفتگی از 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 قرار دهید.
  • از فاصله قبل و بعد از کولون و فاصله بعد از نوع زیرین قبل از بریس باز استفاده کنید.
  • آخرین مقدار enum ممکن است کاما اضافی نداشته باشد.

اعلامیه های ساختاری

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

  • اگر اعلان‌های ساختار با بسته دیگری به اشتراک گذاشته می‌شوند، اعلان‌ها را به جای تعبیه در یک رابط، در 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;