Guia de estilo de código

O estilo de código HIDL se assemelha ao código C++ na estrutura do Android, com recuos de 4 espaços e nomes de arquivo com maiúsculas e minúsculas. Declarações de pacote, importações e docstrings são semelhantes às de Java, com pequenas modificações.

Os exemplos a seguir para IFoo.hal e types.hal ilustram 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

Nomes de funções, nomes de variáveis ​​e nomes de arquivos devem ser descritivos; evitar abreviaturas excessivas. Trate as siglas como palavras (por exemplo, use INfc em vez de INFC ).

Estrutura de diretórios e nomeação de arquivos

A estrutura de diretórios deve aparecer da seguinte forma:

  • ROOT-DIRECTORY
    • MODULE
      • SUBMODULE (opcional, pode ter mais de um nível)
        • VERSION
          • Android.mk
          • I INTERFACE_1 .hal
          • I INTERFACE_2 .hal
          • I INTERFACE_N .hal
          • types.hal (opcional)

Onde:

  • ROOT-DIRECTORY é:
    • hardware/interfaces para pacotes HIDL principais.
    • vendor/ VENDOR /interfaces para pacotes de fornecedores, onde VENDOR se refere a um fornecedor de SoC ou a um OEM/ODM.
  • MODULE deve ser uma palavra minúscula 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 aninhamento.
  • VERSION deve ser exatamente a mesma versão (major.minor) conforme descrito em Versions .
  • I INTERFACE_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

Nota: Todos os arquivos devem ter permissões não executáveis ​​(no Git).

Nomes de pacotes

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

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

Onde:

  • PACKAGE é o pacote que mapeia para o ROOT-DIRECTORY . Em particular, PACKAGE é:
    • android.hardware para pacotes HIDL principais (mapeamento para hardware/interfaces ).
    • vendor. VENDOR .hardware para pacotes de fornecedores, onde VENDOR se refere a um fornecedor de SoC ou a um OEM/ODM (mapeamento para vendor/ VENDOR /interfaces ).
  • MODULE [. SUBMODULE [. SUBMODULE […]]]@ VERSION são exatamente os mesmos nomes de pastas na estrutura descrita em Estrutura de diretórios .
  • Os nomes dos pacotes devem ser minúsculos. Se tiverem mais de uma palavra, as palavras devem 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 pacotes.

Versões

As versões devem ter o seguinte formato:

MAJOR.MINOR

Tanto a versão MAJOR quanto a versão MINOR devem ser um único inteiro. HIDL usa regras de versão semântica .

Importações

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

  • Importações de pacote inteiro: 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 Package names . O types.hal do pacote atual (se existir) é importado automaticamente (não o importe explicitamente).

Nomes totalmente qualificados (FQNs)

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 pacote. Um FQN não deve 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 completo.

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 recuada. Agrupe as importações na seguinte ordem:

  1. Outros pacotes android.hardware (use nomes totalmente qualificados).
  2. Outro vendor. VENDOR Pacotes vendor. VENDOR (use nomes totalmente qualificados).
    • Cada fornecedor deve ser um grupo.
    • Encomende 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 interface

Os nomes de interface devem começar com um I , seguido por um nome UpperCamelCase / PascalCase . Uma interface com o nome IFoo deve ser definida no arquivo IFoo.hal . Este arquivo pode conter definições apenas para a interface IFoo (a interface I NAME deve estar em I NAME .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 de campo de estrutura/união

Para nomes de campo struct/union, use lowerCamelCase . Exemplo:

struct FooReply {
    vec<uint8_t> replyData;
}

Nomes de tipos

Os nomes de tipo referem-se a definições de struct/union, definições de tipo enum e typedef s. Para esses nomes, use UpperCamelCase / PascalCase . Exemplos:

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

Valores de enumeração

Os valores de enumeração devem ser UPPER_CASE_WITH_UNDERSCORES . Ao passar valores enum como argumentos de função e retorná-los como retornos de função, use o tipo enum 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 enum é declarado explicitamente após os dois pontos. Como não depende do compilador, usar o tipo de enumeração real é mais claro.

Para nomes totalmente qualificados para valores enum, dois pontos são usados ​​entre o nome do tipo enum e o nome do valor enum:

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

Não deve haver espaços dentro de um nome totalmente qualificado. Use um nome totalmente qualificado somente quando necessário e omita as 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 bons.

// This is a single line comment
/* This is also single line comment */
/** This is documentation comment */
  • Use /* */ para comentários. Embora o HIDL suporte // para comentários, eles são desencorajados porque não aparecem na saída gerada.
  • Use /** */ para documentação gerada. Eles podem ser aplicados apenas a declarações de tipo, método, campo e valor de 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. Finalize o comentário com */ em uma linha separada, alinhando os asteriscos. Exemplo:
    /**
     * My multi-line
     * comment
     */
    
  • O aviso de licenciamento e os logs de alterações devem iniciar 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 devem se alinhar). Exemplo:
    /*
     * Copyright (C) 2017 The Android Open Source Project
     * ...
     */
    
    /*
     * Changelog:
     * ...
     */
    

Comentários do arquivo

Inicie cada arquivo com o aviso de licenciamento apropriado. Para HALs principais, esta deve ser a licença Apache AOSP em development/docs/copyright-templates/c.txt . Lembre-se de atualizar o ano e usar comentários de várias linhas no estilo /* */ conforme explicado acima.

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

Comentários de TODO

TODOs devem incluir a string TODO em maiúsculas seguida de dois pontos. Exemplo:

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

Comentários TODO são permitidos apenas durante o desenvolvimento; eles não devem existir em interfaces publicadas.

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

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

Docstrings para interfaces devem descrever mecanismos gerais da interface, lógica de design, propósito, etc. Docstrings para funções devem ser específicas para a função (documentação em nível de pacote vai em um arquivo README no diretório de pacotes).

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

Você deve adicionar @param s e @return s para cada parâmetro/valor de retorno:

  • @param deve ser adicionado para cada parâmetro. Deve ser seguido pelo nome do parâmetro e depois pela docstring.
  • @return deve ser adicionado para cada valor de retorno. Deve ser seguido pelo nome do valor de retorno e depois 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);

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 devem conter espaços em branco.
  • Espaços vs. tabulações . Use apenas espaços.
  • Tamanho do recuo . Use 4 espaços para blocos e 8 espaços para quebras de linha
  • Reforço . Exceto para valores de anotação , uma chave de abertura vai na mesma linha do código anterior, mas uma chave de fechamento e o ponto e vírgula seguinte ocupam a linha inteira. Exemplo:
    interface INfc {
        close();
    };
    

Declaração do pacote

A declaração do pacote deve estar no topo do arquivo após o aviso de licença, deve ocupar toda a linha e não deve ser recuada. Os pacotes são declarados usando o seguinte formato (para formatação de nome, consulte Nomes de pacote ):

package PACKAGE-NAME;

Exemplo:

package android.hardware.nfc@1.0;

Declarações de função

O nome da função, parâmetros, generates e valores de retorno devem estar na mesma linha se eles 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 valores de retorno no mesmo nível de recuo e diferencie generate para ajudar o leitor a ver rapidamente os parâmetros e os 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.
  • Sem 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 há alimentação de linha entre eles.
  • Se generates estiver na mesma linha do parêntese de fechamento anterior, use um espaço anterior. Se generates está na mesma linha que o próximo parêntese aberto, siga com um espaço.
  • Alinhe todos os parâmetros e valores de retorno (se possível).
  • O recuo padrão é de 4 espaços.
  • Os parâmetros agrupados são alinhados aos primeiros parâmetros da linha anterior, caso contrário, eles têm um recuo de 8 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

Certifique-se de que uma anotação ocupe toda a linha. Exemplos:

/* Good */
@entry
@exit

/* Bad */
@entry @exit

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

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

Se todo o array de valores não couber na mesma linha, coloque quebras de linha após chaves abertas { e após cada vírgula dentro do array. Coloque parênteses de fechamento imediatamente após o último valor. Não coloque as chaves se houver apenas um valor.

Se a matriz de valores inteira puder caber na mesma linha, não use espaços após abrir chaves 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 deve haver linhas vazias entre as anotações e a declaração da função. Exemplos:

/* Good */
@entry
foo();

/* Bad */
@entry

foo();

Declarações de enumeração

Use as seguintes regras para declarações enum:

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

Declarações de estrutura

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 embutir dentro de uma interface.
  • Use um espaço após o nome do tipo de estrutura antes da chave de abertura.
  • Alinhar nomes de campo (opcional). Exemplo:
    struct MyStruct {
        vec<uint8_t>   data;
        int32_t        someInt;
    }
    

Declarações de matriz

Não coloque espaços entre o seguinte:

  • Tipo de elemento e colchete aberto.
  • Abra o colchete e o tamanho da matriz.
  • Tamanho da matriz e colchetes próximos.
  • Fechar colchete e o próximo colchete aberto, se existir 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 o seguinte:

  • vec e suporte de ângulo aberto.
  • Colchete de ângulo aberto e tipo de elemento ( Exceção: o tipo de elemento também é um vec ).
  • Tipo de elemento e colchete angular fechado ( 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;