Руководство по стилю кода

Стиль кода 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
          • 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 — это точно такие же имена папок в структуре, описанной в разделе «Структура каталогов» .
  • Имена пакетов должны быть в нижнем регистре. Если они состоят из более чем одного слова, слова следует либо использовать как подмодули, либо записать в snake_case .
  • Никакие пробелы не допускаются.

FQN всегда используется в объявлениях пакетов.

Версии

Версии должны иметь следующий формат:

MAJOR.MINOR

И MAJOR , и MINOR версия должны быть одним целым числом. HIDL использует правила семантического управления версиями .

Импорт

Импорт имеет один из следующих трех форматов:

  • Импорт всего пакета: import PACKAGE-NAME ;
  • Частичный импорт: import PACKAGE-NAME :: UDT ; (или, если импортируемый тип находится в том же пакете, import UDT ;
  • Импорт только типов: import PACKAGE-NAME ::types;

PACKAGE-NAME соответствует формату, указанному в Packagenames . types.hal текущего пакета (если он существует) импортируется автоматически (не импортируйте его явно).

Полные имена (FQN)

Используйте полные имена для импорта определяемого пользователем типа только при необходимости. Опустите PACKAGE-NAME , если тип импорта находится в том же пакете. Полное доменное имя не должно содержать пробелов. Пример полного имени:

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

Введите имена

Имена типов относятся к определениям структур или объединений, определениям типов перечислений и typedef s. Для этих имен используйте 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:
     * ...
     */
    

Комментарии к файлам

Начинайте каждый файл с соответствующего уведомления о лицензии. Для основных HAL это должна быть лицензия AOSP Apache в development/docs/copyright-templates/c.txt . Не забудьте обновить год и использовать многострочные комментарии в стиле /* */ , как описано выше.

При желании вы можете разместить пустую строку после уведомления о лицензии, за которой следует журнал изменений/информация о версиях. Используйте многострочные комментарии в стиле /* */ как описано выше, поместите пустую строку после журнала изменений, а затем добавьте объявление пакета.

TODO комментарии

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

Правила форматирования

Общие правила форматирования включают в себя:

  • Длина линии . Каждая строка текста должна содержать не более 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;