Guia de estilo de código

O estilo de código HIDL é semelhante ao código C++ do framework do Android, com 4 espaços recuos e nomes de arquivo com letras maiúsculas e minúsculas. Declarações, importações e docstrings de pacotes são semelhantes aos do Java, com pequenas modificações.

Os seguintes exemplos para IFoo.hal e types.hal ilustrar os estilos de código HIDL e fornecer links rápidos com detalhes sobre cada estilo (IFooClientCallback.hal, IBar.hal e IBaz.hal foram omitidos).

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

Convenções de nomenclatura

Os nomes de funções, variáveis e nomes de arquivos devem ser descritivos. evitar abreviatura excessiva. Trate acrônimos como palavras (por exemplo, use INfc de INFC).

Estrutura de diretórios e nomenclatura de arquivos

A estrutura do diretório deve ser semelhante a esta:

  • ROOT-DIRECTORY
    • MODULE
      • SUBMODULE (opcional, pode ser mais de um) nível)
        • VERSION
          • Android.mk
          • IINTERFACE_1.hal
          • IINTERFACE_2.hal
          • IINTERFACE_N.hal
          • types.hal (opcional)

Em que:

  • ROOT-DIRECTORY é:
    • hardware/interfaces para pacotes HIDL principais.
    • vendor/VENDOR/interfaces para pacotes de fornecedores; em que VENDOR se refere a um fornecedor de SoC ou a um OEM/ODM
  • MODULE precisa ser uma palavra minúscula que descreva o subsistema (por exemplo, nfc). Se mais de uma palavra for necessária, use SUBMODULE aninhada. Pode haver mais de um nível o aninhamento.
  • VERSION precisa ser exatamente a mesma versão (principal.secundária), conforme descrito em Versões.
  • IINTERFACE_X precisa ser o nome da interface com UpperCamelCase/PascalCase (por exemplo, INfc) conforme descrito em Nomes de interface.

Exemplo:

  • hardware/interfaces
    • nfc
      • 1.0
        • Android.mk
        • INfc.hal
        • INfcClientCallback.hal
        • types.hal

Observação: todos os arquivos devem ter arquivos (no Git).

Nomes de pacote

Os nomes de pacotes precisam usar o seguinte nome totalmente qualificado (FQN) (referido como PACKAGE-NAME):

PACKAGE.MODULE[.SUBMODULE[.SUBMODULE[…]]]@VERSION

Em que:

  • PACKAGE é o pacote que mapeia para o ROOT-DIRECTORY. Especificamente, PACKAGE é:
    • android.hardware para os principais pacotes HIDL (mapeando para hardware/interfaces).
    • vendor.VENDOR.hardware para pacotes de fornecedores, em que VENDOR refere-se a um fornecedor de SoC ou a um OEM/ODM (mapeamento para vendor/VENDOR/interfaces).
  • MODULE[.SUBMODULE[.SUBMODULE[…]]]@VERSION têm exatamente os mesmos nomes de pastas na estrutura descrita em Estrutura do diretório.
  • Os nomes dos pacotes precisam estar em letras minúsculas. Se tiverem mais de uma palavra, o as palavras precisam ser usadas como submódulos ou escritas em snake_case.
  • Não são permitidos espaços.

O FQN é sempre usado em declarações de pacote.

Versões

As versões devem ter o seguinte formato:

MAJOR.MINOR

As versões de MAJOR e MINOR precisam ser únicas inteiro. O HIDL usa tipos semânticos controle de versões.

Importações

Uma importação tem um dos três formatos a seguir:

  • Importações de todo o pacote: import PACKAGE-NAME;
  • Importações parciais: import PACKAGE-NAME::UDT; (ou, se o valor importado o tipo está no mesmo pacote,import UDT;
  • Importações somente de tipos: import PACKAGE-NAME::types;

O PACKAGE-NAME segue o formato Nomes de pacote. O valor types.hal (se existir) é importado automaticamente (não importar) explicitamente).

Nomes totalmente qualificados (FQNs, na sigla em inglês)

Use nomes totalmente qualificados para uma importação de tipo definido pelo usuário somente quando necessário. Omita PACKAGE-NAME se o tipo de importação estiver no mesmo . Um FQN não pode conter espaços. Exemplo de um nome totalmente qualificado:

android.hardware.nfc@1.0::INfcClientCallback

Em outro arquivo em android.hardware.nfc@1.0, consulte o acima da interface como INfcClientCallback. Caso contrário, use apenas o nome totalmente qualificado.

Como agrupar e ordenar importações

Use uma linha vazia após a declaração do pacote (antes das importações). Cada importação deve ocupar uma única linha e não deve ser recuado. Importações de grupos na seguinte ordem:

  1. Outros pacotes android.hardware (usam nomes totalmente qualificados).
  2. Outros pacotes vendor.VENDOR (use totalmente qualificados) nomes de domínio).
    • Cada fornecedor deve ser um grupo.
    • Ordenar fornecedores em ordem alfabética.
  3. Importa de outras interfaces no mesmo pacote (use nomes simples).

Use uma linha vazia entre os grupos. Dentro de cada grupo, classifique as importações em ordem alfabética. Exemplo:

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;

Nomes de interface

Os nomes de interface precisam começar com I, seguido por uma UpperCamelCase/PascalCase. Uma interface com nome IFoo precisa ser definido no arquivo IFoo.hal. Este arquivo pode conter definições apenas para a interface IFoo (a interface INAME precisa estar em INAME.hal).

Funções

Para nomes de funções, argumentos e nomes de variáveis de retorno, use lowerCamelCase: Exemplo:

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

Nomes dos campos de estrutura e união

Para nomes de campos de struct ou união, use lowerCamelCase. Exemplo:

struct FooReply {
    vec<uint8_t> replyData;
}

Nomes de tipo

Os nomes de tipo se referem a definições de struct ou união, definições de tipo de enum e typedefs. Para esses nomes, use UpperCamelCase/PascalCase. Exemplos:

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

Valores de enumeração

Os valores de enumeração precisam ser UPPER_CASE_WITH_UNDERSCORES. Ao passar valores enum como argumentos de função e retornando-os como retornos da função, use o tipo de enumeração real (não o tipo inteiro subjacente). Exemplo:

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

Observação:o tipo subjacente de um tipo enumerado é explicitamente declarado depois dos dois pontos. Como não depende do compilador, usar o tipo de tipo enumerado real é mais claro.

Para nomes totalmente qualificados para valores de enumeração, são usados dois-pontos. entre o nome do tipo e do valor da enumeração:

PACKAGE-NAME::UDT[.UDT[.UDT[…]]:ENUM_VALUE_NAME

Não pode haver espaços dentro de um nome totalmente qualificado. Use um endereço de e-mail nomear somente quando necessário e omitir as partes desnecessárias. Exemplo:

android.hardware.foo@1.0::IFoo.IFooInternal.FooEnum:ENUM_OK

Comentários

Para um comentário de uma única linha, //, /* */ e /** */ que não tem problema.

// This is a single line comment
/* This is also single line comment */
/** This is documentation comment */
  • Use /* */ para comentários. O HIDL oferece suporte a // para comentários, elas não são recomendadas, porque não aparecem na saída gerada.
  • Use /** */ para a documentação gerada. Elas podem ser aplicadas somente para declarações de tipo, método, campo e enumeração. Exemplo:
    /** 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,
        ...
    }
    
  • Inicie comentários de várias linhas com /** em uma linha separada. Use * no início de cada linha. Encerre o comentário com */ em uma linha separada, alinhando os asteriscos aos asteriscos. Exemplo:
    /**
     * My multi-line
     * comment
     */
    
  • O aviso de licenciamento e os registros de alterações precisam começar uma nova linha com /* (um único asterisco), use * no início de cada linha e coloque */ na última linha por conta própria (os asteriscos precisam estar alinhados). Exemplo:
    /*
     * Copyright (C) 2017 The Android Open Source Project
     * ...
     */
    
    /*
     * Changelog:
     * ...
     */
    

Comentários em arquivos

Comece cada arquivo com o aviso de licenciamento apropriado. Para HALs de núcleo, este é a licença do AOSP Apache no development/docs/copyright-templates/c.txt Atualize o ano e use comentários de várias linhas no estilo /* */ conforme explicado acima.

Você tem a opção de colocar uma linha vazia após o aviso de licença, seguida por informações de log de mudanças/controle de versão. Usar o estilo /* */ comentários de várias linhas, como explicado acima, coloque a linha vazia após o o log de mudanças e a declaração do pacote.

Comentários TODO

Os TODOs precisam incluir a string TODO em letras maiúsculas seguidas por uma dois-pontos. Exemplo:

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

Comentários TODO são permitidos somente durante o desenvolvimento. eles precisam não existem em interfaces publicadas.

Comentários sobre interface e função (docstrings)

Use /** */ para docstrings de linha única e de várias linhas. Não usar // para docstrings.

As Docstrings para interfaces devem descrever mecanismos gerais do interface, justificativa, propósito etc. Docstrings para funções devem ser específica da função (a documentação no nível do pacote se encontra em um arquivo README no diretório do pacote).

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

É necessário adicionar @params e @returns para cada parâmetro/valor de retorno:

  • @param precisa ser adicionado para cada parâmetro. Deve ser seguido pelo nome do parâmetro e, em seguida, pelo docstring.
  • @return precisa ser adicionado para cada valor de retorno. Ela deve ser seguido pelo nome do valor de retorno e, em seguida, pela docstring.

Exemplo:

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

Regras de formatação

As regras gerais de formatação incluem:

  • Comprimento da linha. Cada linha de texto deve ter no máximo 100 colunas.
  • Espaços em branco. Nenhum espaço em branco à direita nas linhas. linhas vazias não pode conter espaços em branco.
  • Espaços x guias. Use apenas espaços.
  • Tamanho do recuo. Use 4 espaços para blocos e 8 espaços para quebras de linha
  • Contração. Exceto pela anotação valores, uma chave open vai na mesma linha que a anterior mas uma chave close e o seguinte ponto e vírgula ocupa da linha inteira. Exemplo:
    interface INfc {
        close();
    };
    

Declaração do pacote

A declaração do pacote precisa estar na parte de cima do arquivo após a licença deve ocupar a linha inteira e não deve ser recuado. Os pacotes são declarado com o seguinte formato (para formatação de nomes, consulte Nomes de pacote):

package PACKAGE-NAME;

Exemplo:

package android.hardware.nfc@1.0;

Declarações de função

Nome da função, parâmetros, generates e valores de retorno precisam estejam na mesma linha se encaixarem. Exemplo:

interface IFoo {
    /** ... */
    easyMethod(int32_t data) generates (int32_t result);
};

Se eles não couberem na mesma linha, tente colocar parâmetros e retornar valores no mesmo nível de recuo e distinguir generate para ajudar o leitor a vê rapidamente os parâmetros e valores de retorno. Exemplo:

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

Detalhes adicionais:

  • Um parêntese aberto está sempre na mesma linha que o nome da função.
  • Não há espaços entre o nome da função e o parêntese aberto.
  • Não há espaços entre parênteses e parâmetros, exceto quando não há feeds de linha entre eles.
  • Se generates estiver na mesma linha que o fechamento anterior parênteses, use um espaço no início. Se generates estiver no mesmo como o próximo parêntese aberto, seguido por um espaço.
  • Alinhe todos os parâmetros e retorne valores (se possível).
  • O recuo padrão tem quatro espaços.
  • Os parâmetros com wrapper estão alinhados aos primeiros parâmetros na linha anterior, caso contrário, terão um recuo de oito espaços.

Anotações

Use o seguinte formato para anotações:

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

Classifique as anotações em ordem alfabética e use espaços em torno de sinais de igual. Exemplo:

@callflow(key = value)
@entry
@exit

Garantir que uma anotação ocupe a linha inteira. Exemplos:

/* Good */
@entry
@exit

/* Bad */
@entry @exit

Se as anotações não couberem na mesma linha, recue com oito espaços. Exemplo:

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

Se toda a matriz de valores não couber na mesma linha, coloque quebras de linha após abra as chaves { e depois de cada vírgula dentro da matriz. Lugar fechado parênteses imediatamente após o último valor. Não coloque as chaves se houver apenas um valor.

Se toda a matriz de valores couber na mesma linha, não use espaços após de abertura e antes de fechar chaves e use um espaço após cada vírgula. Exemplos:

/* Good */
@callflow(key = {"val", "val"})

/* Bad */
@callflow(key = { "val","val" })

NÃO pode haver linhas vazias entre as anotações e a função declaração de serviço. Exemplos:

/* Good */
@entry
foo();

/* Bad */
@entry

foo();

Declarações de enumeração

Use as seguintes regras para declarações de tipo enumerado:

  • Se as declarações de tipo enumerado forem compartilhadas com outro pacote, coloque-as em types.hal em vez de incorporar em uma interface.
  • Use um espaço antes e depois dos dois-pontos e um espaço após o tipo subjacente. antes da chave aberta.
  • O último valor de tipo enumerado pode não ter uma vírgula a mais.

Declarações de struct

Use as regras abaixo para declarações de struct:

  • Se as declarações de struct forem compartilhadas com outro pacote, coloque-as em types.hal em vez de incorporar em uma interface.
  • Use um espaço após o nome do tipo de struct antes da chave aberta.
  • Alinhar os nomes dos campos (opcional). Exemplo:
    struct MyStruct {
        vec<uint8_t>   data;
        int32_t        someInt;
    }
    

Declarações de matriz

Não coloque espaços entre:

  • Tipo de elemento e colchete aberto.
  • Abra o colchete e o tamanho da matriz.
  • Tamanho da matriz e colchete fechado.
  • Fechar colchete e o próximo colchete aberto, se houver mais de um existe.

Exemplos:

/* Good */
int32_t[5] array;

/* Good */
int32_t[5][6] multiDimArray;

/* Bad */
int32_t [ 5 ] [ 6 ] array;

Vetores

Não coloque espaços entre:

  • vec e colchete angular de abertura.
  • Colchete angular e tipo de elemento (Exceção: o tipo do elemento também é um vec).
  • Tipo de elemento e colchete angular de fechamento (Exceção: o tipo do elemento também é um vec).

Exemplos:

/* 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;