HIDL 코드 스타일은 4개의 공백 들여쓰기 및 대소문자가 혼합된 파일 이름을 비롯해 Android 프레임워크의 C++ 코드와 유사합니다. 패키지 선언, 가져오기 및 docstring은 약간 변경된 부분이 있을 뿐, 자바와 유사합니다.
IFoo.hal
및 types.hal
에 대한 다음의 예시는 HIDL 코드 스타일을 보여 주며, 각 스타일의 세부정보에 대한 빠른 링크를 제공합니다(IFooClientCallback.hal
, IBar.hal
및 IBaz.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을 의미합니다.
- 핵심 HIDL 패키지의 경우
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
여기서
PACKAGE
는ROOT-DIRECTORY
로 매핑하는 패키지입니다. 특히PACKAGE
는 다음과 같습니다.- 핵심 HIDL 패키지의 경우
android.hardware
(hardware/interfaces
로 매핑)입니다. - 공급업체 패키지의 경우
vendor.VENDOR.hardware
로, 여기서VENDOR
는 SoC 공급업체 또는 OEM/ODM(vendor/VENDOR/interfaces
로 매핑)을 의미합니다.
- 핵심 HIDL 패키지의 경우
MODULE[.SUBMODULE[.SUBMODULE[…]]]@VERSION
은 디렉터리 구조에 명시된 구조에 있는 것과 정확히 동일한 폴더 이름입니다.- 패키지 이름은 소문자여야 합니다. 단어가 2개 이상인 경우 해당 단어는 하위 모듈로 사용되거나
snake_case
로 작성되어야 합니다. - 공백은 허용되지 않습니다.
FQN은 항상 패키지 선언에 사용됩니다.
버전
버전은 다음과 같은 형식이어야 합니다.
MAJOR.MINOR
MAJOR 및 MINOR 버전은 모두 하나의 정수여야 합니다. 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
으로 참조하세요. 이러한 경우가 아니면 정규화된 이름만 사용하세요.
가져오기 그룹화 및 정렬
패키지 선언 뒤(가져오기 앞)에 빈 줄을 사용합니다. 각 가져오기는 한 줄을 차지해야 하며 들여쓰기해서는 안 됩니다. 다음 순서로 그룹을 가져옵니다.
- 기타
android.hardware
패키지(정규화된 이름 사용) - 기타
vendor.VENDOR
패키지(정규화된 이름 사용)- 각 공급업체는 그룹이어야 합니다.
- 공급업체를 알파벳순으로 정렬합니다.
- 동일한 패키지의 다른 인터페이스에서 가져옵니다(간단한 이름 사용).
그룹 간에는 빈 줄을 사용합니다. 각 그룹 내의 가져오기를 알파벳순으로 정렬합니다. 예를 들면 다음과 같습니다.
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;