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

Стиль кода 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), как описано в Versions .
  • 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 .
  • Пробелы не допускаются.

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

Версии

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

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 Пакеты 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 {
    /*...*/
};

Значения перечисления

Значения перечисления должны быть 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 s и @return s для каждого параметра/возвращаемого значения:

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

Объявления перечисления

Используйте следующие правила для объявлений enum:

  • Если объявления enum используются совместно с другим пакетом, поместите объявления в 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;