코드 스타일 가이드

HIDL 코드 스타일은 4개의 공백 들여쓰기 및 대소문자가 혼합된 파일 이름을 비롯해 Android 프레임워크의 C++ 코드와 유사합니다. 패키지 선언, 가져오기 및 docstring은 약간 변경된 부분이 있을 뿐, 자바와 유사합니다.

IFoo.haltypes.hal에 대한 다음의 예시는 HIDL 코드 스타일을 보여 주며, 각 스타일의 세부정보에 대한 빠른 링크를 제공합니다(IFooClientCallback.hal, IBar.halIBaz.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
          • IINTERFACE_1.hal
          • IINTERFACE_2.hal
          • IINTERFACE_N.hal
          • types.hal(선택사항)

여기서

  • ROOT-DIRECTORY는 다음과 같습니다.
    • 핵심 HIDL 패키지의 경우 hardware/interfaces입니다.
    • 공급업체 패키지의 경우 vendor/VENDOR/interfaces로 여기서 VENDOR는 SoC 공급업체 또는 OEM/ODM을 의미합니다.
  • MODULE은 하위 시스템을 설명하는 하나의 소문자 단어여야 합니다(예: nfc). 단어가 두 개 이상 필요한 경우 중첩된 SUBMODULE을 사용합니다. 중첩 수준은 둘 이상일 수 있습니다.
  • VERSION버전에 명시된 것과 정확히 동일한 버전(major.minor)이어야 합니다.
  • IINTERFACE_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

여기서

  • PACKAGEROOT-DIRECTORY로 매핑하는 패키지입니다. 특히 PACKAGE는 다음과 같습니다.
    • 핵심 HIDL 패키지의 경우 android.hardware(hardware/interfaces로 매핑)입니다.
    • 공급업체 패키지의 경우 vendor.VENDOR.hardware로, 여기서 VENDOR는 SoC 공급업체 또는 OEM/ODM(vendor/VENDOR/interfaces로 매핑)을 의미합니다.
  • MODULE[.SUBMODULE[.SUBMODULE[…]]]@VERSION디렉터리 구조에 명시된 구조에 있는 것과 정확히 동일한 폴더 이름입니다.
  • 패키지 이름은 소문자여야 합니다. 단어가 2개 이상인 경우 해당 단어는 하위 모듈로 사용되거나 snake_case로 작성되어야 합니다.
  • 공백은 허용되지 않습니다.

FQN은 항상 패키지 선언에 사용됩니다.

버전

버전은 다음과 같은 형식이어야 합니다.

MAJOR.MINOR

MAJORMINOR 버전은 모두 하나의 정수여야 합니다. HIDL은 시맨틱 버전 관리 규칙을 사용합니다.

가져오기

가져올 때는 다음 세 가지 형식 중 하나를 사용합니다.

  • 전체 패키지 가져오기: import PACKAGE-NAME;
  • 부분 가져오기: import PACKAGE-NAME::UDT;, 가져온 유형이 동일한 패키지에 있는 경우에는 import UDT;
  • 유형만 가져오기: import PACKAGE-NAME::types;

PACKAGE-NAME패키지 이름의 형식을 따릅니다. 현재 패키지의 types.hal이 있는 경우 자동으로 가져오므로 이를 별도의 방법으로 가져오지 마세요.

정규화된 이름(FQN)

필요한 경우에만 사용자 정의 유형 가져오기에 정규화된 이름을 사용합니다. 가져오기 유형이 동일한 패키지에 있는 경우 PACKAGE-NAME을 생략합니다. FQN은 공백을 포함할 수 없습니다. 정규화된 이름의 예시는 다음과 같습니다.

android.hardware.nfc@1.0::INfcClientCallback

android.hardware.nfc@1.0을 사용하는 다른 파일에서는 위 인터페이스를 INfcClientCallback으로 참조하세요. 이러한 경우가 아니면 정규화된 이름만 사용하세요.

가져오기 그룹화 및 정렬

패키지 선언 뒤(가져오기 앞)에 빈 줄을 사용합니다. 각 가져오기는 한 줄을 차지해야 하며 들여쓰기해서는 안 됩니다. 다음 순서로 그룹을 가져옵니다.

  1. 기타 android.hardware 패키지(정규화된 이름 사용)
  2. 기타 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 인터페이스의 정의만 포함할 수 있습니다(INAME 인터페이스는 INAME.hal에 있어야 함).

함수

함수 이름, 인수 및 반환 변수 이름의 경우 lowerCamelCase를 사용합니다. 예를 들면 다음과 같습니다.

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

구조체/공용체 필드 이름

구조체/공용체 필드 이름의 경우 lowerCamelCase를 사용합니다. 예를 들면 다음과 같습니다.

struct FooReply {
    vec<uint8_t> replyData;
}

유형 이름

유형 이름은 구조체/공용체 정의, Enum 형식 정의 및 typedef를 참조합니다. 이러한 이름을 사용하려면 UpperCamelCase/PascalCase를 사용합니다. 예를 들면 다음과 같습니다.

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

Enum 값

Enum 값은 UPPER_CASE_WITH_UNDERSCORES이어야 합니다. Enum 값을 함수 인수로 전달하고 이를 함수 반환으로 반환하는 경우 기본 정수 형식이 아닌 실제 enum 형식을 사용합니다. 예를 들면 다음과 같습니다.

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

참고: enum 형식의 기본 형식은 콜론 뒤에 명시적으로 선언됩니다. 컴파일러에 의존하지 않으므로 실제 enum 형식을 사용하면 더 명확해집니다.

정규화된 이름의 enum 값의 경우 enum 형식 이름과 enum 값 이름 사이에 콜론이 사용됩니다.

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은 댓글에 //를 지원하지만, 생성된 출력에는 나타나지 않으므로 권장되지 않습니다.
  • 생성된 문서에는 /** */를 사용합니다. 유형, 메서드, 필드 및 enum 값 선언에만 적용할 수 있습니다. 예를 들면 다음과 같습니다.
    /** 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의 경우 development/docs/copyright-templates/c.txt에 있는 AOSP Apache 라이선스여야 합니다. 연도를 업데이트하고 위에 설명된 대로 /* */ 스타일의 여러 줄 댓글을 사용해야 합니다.

선택적으로 라이선스 고지 다음에 빈 줄을 삽입하고 그 뒤에 변경 로그/버전 관리 정보를 추가할 수 있습니다. 위에서 설명한 대로 /* */ 스타일의 여러 줄 댓글을 사용하고, 변경 로그 뒤에 빈 줄을 삽입한 다음 그 뒤에 패키지 선언을 추가합니다.

TODO 댓글

TODO에는 모두 대문자로 된 TODO 문자열에 이어 콜론이 포함되어야 합니다. 예를 들면 다음과 같습니다.

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

TODO 댓글은 개발 중인 경우에만 허용되며 게시된 인터페이스에 존재해서는 안 됩니다.

인터페이스/함수 댓글(docstring)

여러 줄 및 한 줄 docstring에는 /** */를 사용합니다. docstring에는 //를 사용하지 않습니다.

인터페이스에 관한 docstring은 인터페이스의 일반적인 메커니즘, 설계 원리, 목적 등을 설명해야 합니다. 함수에 관한 docstring은 함수와 관련된 내용이어야 합니다. 패키지 수준 문서는 패키지 디렉터리의 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@return을 추가해야 합니다.

  • 각 매개변수에 @param을 추가해야 합니다. 이다음에 매개변수의 이름, 그다음에는 docstring이 와야 합니다.
  • 각 반환 값에 @return을 추가해야 합니다. 이다음에 반환 값의 이름, 그다음에는 docstring이 와야 합니다.

예를 들면 다음과 같습니다.

/**
 * 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 선언에는 다음 규칙을 사용합니다.

  • Enum 선언이 다른 패키지와 공유되는 경우 선언을 인터페이스 내부에 삽입하는 것이 아니라 types.hal에 넣습니다.
  • 콜론 앞뒤에 공백 하나, 기본 형식 뒤, 여는 중괄호 앞에 공백을 사용합니다.
  • 마지막 enum 값에는 쉼표를 추가해도 되고 추가하지 않아도 됩니다.

구조체 선언

구조체 선언에는 다음 규칙을 사용합니다.

  • 구조체 선언이 다른 패키지와 공유되는 경우 선언을 인터페이스 내부에 삽입하는 것이 아니라 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;