Guia de estilo de código

O estilo de código HIDL se assemelha ao código C++ na estrutura Android, com recuos de 4 espaços e nomes de arquivos com letras maiúsculas e minúsculas. Declarações de pacotes, importações e docstrings são semelhantes aos 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 de 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; evite abreviações excessivas. Trate os 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 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 for necessária mais de uma palavra, use SUBMODULE aninhado. Pode haver mais de um nível de aninhamento.
  • VERSION deve ser exatamente a mesma versão (major.minor) descrita em Versões .
  • 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 pasta na estrutura descrita em Estrutura de diretórios .
  • Os nomes dos pacotes devem estar em letras minúsculas. 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 MINOR devem ser um único número inteiro. HIDL usa regras de versionamento semântico .

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 Nomes de pacotes . O types.hal do pacote atual (se existir) é importado automaticamente (não importe-o explicitamente).

Nomes totalmente qualificados (FQNs)

Use nomes completos 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, utilize apenas o nome completo.

Agrupando e ordenando 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.
    • Ordene os 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 das interfaces devem começar com I , seguido por um nome UpperCamelCase / PascalCase . Uma interface com 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 campos de estrutura/união

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

struct FooReply {
    vec<uint8_t> replyData;
}

Digite nomes

Os nomes dos tipos referem-se a definições de struct/união, 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 enum 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
};

Nota: O tipo subjacente de um tipo enum é declarado explicitamente após os dois pontos. Como não depende do compilador, o uso do tipo enum real é mais claro.

Para nomes completos 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 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 /** */ estão bem.

// 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 enum. 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. Termine o comentário com */ em uma linha separada, alinhando os asteriscos. Exemplo:
    /**
     * My multi-line
     * comment
     */
    
  • O aviso de licenciamento e os changelogs devem começar uma nova linha com /* (um único asterisco), usar * no início de cada linha e colocar */ na última linha sozinho (os asteriscos devem estar alinhados). 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 AOSP Apache 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 informações de changelog/versionamento. Use comentários de várias linhas no estilo /* */ conforme explicado acima, coloque a linha vazia após o changelog e siga com a declaração do pacote.

TODO comentários

TODOs devem incluir a string TODO em letras 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 multilinhas 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 (a documentação em nível de pacote vai 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();
};

Você deve adicionar @param se @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 nas linhas; linhas vazias não devem conter espaços em branco.
  • Espaços versus guias . Use apenas espaços.
  • Tamanho do recuo . Use 4 espaços para blocos e 8 espaços para quebras de linha
  • Preparação . Exceto para valores de anotação , uma chave de abertura fica 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 de 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 nomes, 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 devem 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 distinguir 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);
}

Detalhes adicionais:

  • Um parêntese aberto está sempre na mesma linha do nome da função.
  • Não há espaços entre o nome da função e os parênteses abertos.
  • Não há espaços entre parênteses e parâmetros , exceto quando há avanços de linha entre eles.
  • Se generates estiver na mesma linha do parêntese de fechamento anterior, use um espaço anterior. Se generates estiver na mesma linha do próximo parêntese aberto, coloque um espaço em seguida.
  • Alinhe todos os parâmetros e retorne valores (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 terão 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 dos 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 toda a matriz de valores não couber na mesma linha, coloque quebras de linha após colchetes { e após cada vírgula dentro da matriz. Coloque parênteses de fechamento imediatamente após o último valor. Não coloque colchetes se houver apenas um valor.

Se toda a matriz de valores couber na mesma linha, não use espaços após colchetes abertos e antes de colchetes de fechamento 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 incorporá-las 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 enum pode ou não ter uma vírgula extra.

Declarações estruturais

Use as seguintes regras para declarações struct:

  • Se as declarações struct forem compartilhadas com outro pacote, coloque as declarações em types.hal em vez de incorporar dentro de uma interface.
  • Use um espaço após o nome do tipo de estrutura 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 o seguinte:

  • Tipo de elemento e colchete aberto.
  • Abra colchetes e tamanho da matriz.
  • Tamanho da matriz e colchete próximo.
  • Feche o 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 colchete angular aberto.
  • Abra o colchete angular e o tipo de elemento ( Exceção: o tipo de elemento também é um vec ).
  • Tipo de elemento e colchete angular próximo ( 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;