Guia de estilo de código

O estilo de código HIDL se assemelha ao código C++ no framework do Android, com recuos de quatro espaços e nomes de arquivos com letras maiúsculas e minúsculas. As declarações, importações e docstrings de pacotes são semelhantes às do Java, com pequenas modificações.

Os exemplos a seguir para IFoo.hal e types.hal ilustram os estilos de código HIDL e fornecem links rápidos para 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ção, variáveis e arquivos precisam ser descritivos. Evite abreviações excessivas. Tratar acrônimos como palavras (por exemplo, use INfc em vez de INFC).

Estrutura de diretórios e nomenclatura de arquivos

A estrutura de diretórios vai aparecer assim:

  • ROOT-DIRECTORY
    • MODULE
      • SUBMODULE (opcional, pode haver 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 principais de HIDL.
    • 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 em letras minúsculas que descreva o subsistema (por exemplo, nfc). Se mais de uma palavra for necessária, use SUBMODULE aninhado. Pode haver mais de um nível de anilhamento.
  • O VERSION precisa ser exatamente a mesma versão (major.minor), conforme descrito em Versões.
  • IINTERFACE_X deve 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 precisam ter permissões não executáveis (no Git).

Nomes de pacote

Os nomes de pacote precisam usar o seguinte formato de nome totalmente qualificado (FQN, na sigla em inglês) (chamado de PACKAGE-NAME):

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

Em que:

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

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

Versões

As versões precisam ter o seguinte formato:

MAJOR.MINOR

As versões MAJOR e MINOR precisam ser um único número inteiro. O HIDL usa regras de versionamento semântico.

Importações

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

  • Importações de pacotes inteiros: import PACKAGE-NAME;
  • Importações parciais: import PACKAGE-NAME::UDT; (ou, se o tipo importado estiver no mesmo pacote,import UDT;
  • Importações somente de tipos: import PACKAGE-NAME::types;

O PACKAGE-NAME segue o formato em Nomes de pacotes. O types.hal do pacote atual (se existir) é importado automaticamente (não importe explicitamente).

Nomes totalmente qualificados (FQNs)

Use nomes totalmente qualificados para uma importação de tipo definida pelo usuário somente quando necessário. Omita PACKAGE-NAME se o tipo de importação estiver no mesmo pacote. 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 a interface acima como INfcClientCallback. Caso contrário, use apenas o nome totalmente qualificado.

Agrupar e ordenar importações

Use uma linha vazia após a declaração do pacote (antes das importações). Cada importação precisa ocupar uma única linha e não pode ter recuo. Agrupe as importações na seguinte ordem:

  1. Outros pacotes android.hardware (use nomes totalmente qualificados).
  2. Outros pacotes vendor.VENDOR (use nomes totalmente qualificados).
    • Cada fornecedor precisa ser um grupo.
    • Ordenar os fornecedores em ordem alfabética.
  3. Importações 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 interfaces

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

Funções

Para nomes de função, argumentos e nomes de variáveis de retorno, use lowerCamelCase. Exemplo:

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

Nomes de campos de struct e union

Para nomes de campos de struct ou union, 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 enumeração 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 transmitir valores de tipo enumerado como argumentos de função e retornar como retornos de função, use o tipo de tipo enumerado real (não o tipo de 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 de um tipo de enumeração é declarado explicitamente após os dois-pontos. Como não depende do compilador, o uso do tipo de enumeração real é mais claro.

Para nomes totalmente qualificados de valores de tipo enumerado, um dois-pontos é usado entre o nome do tipo e o nome do valor:

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

Não pode haver espaços dentro de um nome totalmente qualificado. Use um nome totalmente qualificado apenas quando necessário e omita partes desnecessárias. Exemplo:

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

Comentários

Para um comentário de linha única, //, /* */ e /** */ são adequados.

// This is a single line comment
/* This is also single line comment */
/** This is documentation comment */
  • Use /* */ para comentários. Embora o HIDL ofereça suporte a // para comentários, eles não são recomendados porque não aparecem na saída gerada.
  • Use /** */ para a documentação gerada. Elas podem ser aplicadas apenas a declarações de tipo, método, campo e valor de tipo enumerado. 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,
        ...
    }
  • Comece os comentários de várias linhas com /** em uma linha separada. Use * no início de cada linha. Termine o comentário com */ em uma linha separada, alinhando os asteriscos. Exemplo:
    /**
     * My multi-line
     * comment
     */
  • O aviso de licenciamento e os registros de mudanças precisam começar uma nova linha com /* (um único asterisco), usar * no início de cada linha e colocar */ 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 principais, essa deve ser a licença Apache do AOSP em development/docs/copyright-templates/c.txt. Atualize o ano e use comentários de várias linhas no estilo /* */, conforme explicado acima.

Você pode colocar uma linha vazia após o aviso de licença, seguido por informações de registro de alterações/versão. Use comentários de várias linhas no estilo /* */, conforme explicado acima. Coloque a linha vazia após o registro de alterações e siga com a declaração do pacote.

Comentários TODO

Os TODOs precisam incluir a string TODO em todas as letras maiúsculas, seguidas por dois pontos. Exemplo:

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

Os comentários TODO são permitidos apenas durante o desenvolvimento. Eles não podem existir em interfaces publicadas.

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

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

As strings de documentação de interfaces precisam descrever mecanismos gerais da interface, justificativa de design, finalidade etc. As strings de documentação de funções precisam ser específicas para a função (a documentação no nível do pacote vai para 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();
};

Adicione @params e @returns para cada parâmetro/valor de retorno:

  • @param precisa ser adicionado para cada parâmetro. Ele precisa ser seguido pelo nome do parâmetro e depois pelo docstring.
  • @return precisa ser adicionado para cada valor de retorno. Ele deve ser seguido pelo nome do valor de retorno e do 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 de formatação gerais incluem:

  • Comprimento da linha. Cada linha de texto precisa ter no máximo 100 colunas.
  • Espaços em branco. Não há espaços em branco finais nas linhas; as linhas vazias não podem conter espaços em branco.
  • Espaços e tabulações. Use apenas espaços.
  • Tamanho do recuo. Use 4 espaços para blocos e 8 espaços para uniões de linha.
  • Preparação. Exceto para valores de anotação, uma chave aberta vai para a mesma linha que o código anterior, mas uma chave fechada e o ponto e vírgula seguinte ocupam a linha inteira. Exemplo:
    interface INfc {
        close();
    };

Declaração de pacote

A declaração de pacote precisa estar na parte de cima do arquivo após o aviso de licença, ocupar a linha inteira e não ter recuo. Os pacotes são declarados usando o seguinte formato (para formatação de nome, consulte Nomes de pacotes):

package PACKAGE-NAME;

Exemplo:

package android.hardware.nfc@1.0;

Declarações de função

O nome da função, os parâmetros, generates e os valores de retorno precisam estar na mesma linha se couberem. Exemplo:

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

Se eles não couberem na mesma linha, tente colocar parâmetros e valores de retorno no mesmo nível de recuo e diferencie generate para ajudar o leitor a ver 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);
}

Mais detalhes:

  • 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 os parênteses e os parâmetros exceto quando há quebras de linha entre eles.
  • Se generates estiver na mesma linha do parêntese de fechamento anterior, use um espaço antes. Se generates estiver na mesma linha que o próximo parêntese aberto, siga com um espaço.
  • Alinhe todos os parâmetros e retorne valores (se possível).
  • A indentação padrão é de quatro espaços.
  • Os parâmetros agrupados são alinhados aos primeiros parâmetros na linha anterior. Caso contrário, eles têm uma indentação 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 igualdade. Exemplo:

@callflow(key = value)
@entry
@exit

Verifique se uma anotação ocupa toda a linha. 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 a matriz de valores inteira não couber na mesma linha, coloque quebras de linha após chaves abertas { e após cada vírgula dentro da matriz. Coloque o parêntese de fechamento imediatamente após o último valor. Não coloque os colchetes se houver apenas um valor.

Se a matriz de valores inteira couber na mesma linha, não use espaços após chaves abertas e antes de chaves fechadas 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 declaração da função. Exemplos:

/* Good */
@entry
foo();

/* Bad */
@entry

foo();

Declarações de tipo enumerado

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

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

Declarações de struct

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

  • Se as declarações de struct forem compartilhadas com outro pacote, coloque as declarações em types.hal em vez de incorporá-las a uma interface.
  • Use um espaço após o nome do tipo de struct antes da chave de abertura.
  • Alinhe 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 os seguintes elementos:

  • Tipo de elemento e colchete aberto.
  • Abra o colchete e o tamanho da matriz.
  • Tamanho da matriz e fechamento de colchetes.
  • Fechar colchetes e o próximo colchete aberto, se houver mais de uma dimensão.

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 os seguintes elementos:

  • vec e sinal de menor que.
  • Parêntese angular aberto e tipo de elemento (Exceção: o tipo de elemento também é um vec).
  • Tipo de elemento e parêntese angular de fechamento (Exceção: o tipo de 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;