버전 관리

HIDL로 작성된 모든 인터페이스는 버전을 지정해야 합니다. HAL 인터페이스가 게시된 후에는 변경이 불가하며 이후 변경사항이 있는 경우 인터페이스의 새 버전에 적용되어야 합니다. 게시된 특정 인터페이스는 수정할 수 없지만 다른 인터페이스에 의해 확장될 수는 있습니다.

HIDL 코드 구조

HIDL 코드 구성은 사용자 정의 유형, 인터페이스, 패키지로 이루어집니다.

  • 사용자 정의 유형(UDT): HIDL은 구조, 공용체, 열거를 통해 더 복잡한 유형을 작성하는 데 사용할 수 있는 기본 데이터 유형 세트로의 액세스를 제공합니다. UDT는 인터페이스의 메서드에 전달되며 모든 인터페이스에 공통되는 패키지 수준에서 정의하거나 인터페이스에 로컬로 정의할 수 있습니다.
  • 인터페이스: HIDL의 기본 구성요소인 인터페이스는 UDT 및 메서드 선언으로 구성됩니다. 인터페이스는 다른 인터페이스에서 상속할 수도 있습니다.
  • 패키지: 관련 HIDL 인터페이스와 이들이 연산을 실행하는 대상인 데이터 유형을 구성합니다. 패키지는 이름과 버전으로 식별되며 다음을 포함합니다.
    • types.hal이라는 데이터 유형 정의 파일
    • 0개 이상의 인터페이스(각각 자체적인 .hal 파일에 위치)

데이터 유형 정의 파일 types.hal에는 UDT만 포함되며 패키지 수준의 모든 UDT는 단일 파일에 보관됩니다. 타겟 언어로 된 표현은 패키지에 있는 모든 인터페이스에서 사용할 수 있습니다.

버전 관리 원칙

HIDL 패키지(예: android.hardware.nfc)는 특정 버전(예: 1.0)과 관련하여 게시된 후에는 불변입니다. 즉, 변경할 수 없습니다. 패키지의 인터페이스에 관한 수정사항이나 UDT에 관한 변경사항은 다른 패키지에서만 적용될 수 있습니다.

HIDL의 경우 버전 관리는 인터페이스 수준이 아닌 패키지 수준에서 적용되며 패키지의 모든 인터페이스와 UDT는 같은 버전을 공유합니다. 패키지 버전은 패치 수준 및 메타데이터 빌드 구성요소 없이 시맨틱 버전 관리를 따릅니다. 지정된 패키지 내에서 부 버전 범프는 패키지의 새 버전이 이전 패키지와 하위 호환됨을 암시하며 주 버전 범프는 패키지의 새 버전이 이전 패키지와 하위 호환되지 않음을 암시합니다.

개념적으로 패키지는 다음 중 한 가지 방식으로 다른 패키지와 연관될 수 있습니다.

  • 전혀 연관되지 않음
  • 패키지 수준의 하위 호환 확장성. 이는 패키지의 새로운 부 버전 업데이트(다음 상위 버전)에서 발생합니다. 새 패키지는 이전 패키지와 이름 및 주 버전이 같지만 부 버전은 상위입니다. 새 패키지는 기능적으로 이전 패키지의 상위 집합이며 이는 다음을 의미합니다.
    • 상위 패키지의 최상위 인터페이스는 새 패키지에 있지만 인터페이스에는 새 메서드, 새 인터페이스 로컬 UDT(아래에 설명된 인터페이스 수준 확장) 및 types.hal의 새 UDT가 포함될 수 있습니다.
    • 새 패키지에 새 인터페이스를 추가할 수도 있습니다.
    • 상위 패키지의 모든 데이터 유형은 새 패키지에 있으며 이전 패키지의 메서드(재구현되었을 가능성 있음)에 의해 처리될 수 있습니다.
    • 업데이트된 기존 인터페이스의 새로운 메서드 또는 새로운 인터페이스에서 사용할 새로운 데이터 유형이 추가될 수도 있습니다.
  • 인터페이스 수준의 하위 호환 확장성. 새로운 패키지는 핵심 기능이 아닌 추가 기능만 제공하는 논리적으로 구분된 인터페이스로 구성되어 원본 패키지를 확장할 수도 있습니다. 이를 위해 다음과 같이 하는 것이 좋습니다.
    • 새 패키지의 인터페이스는 이전 패키지의 데이터 유형이 필요합니다.
    • 새 패키지의 인터페이스는 하나 이상의 이전 패키지에 속한 인터페이스를 확장할 수 있습니다.
  • 원본 하위 비호환성 확장. 패키지의 주 버전 업데이트이며 두 버전 간에 어떠한 상관관계도 필요하지 않습니다. 상관관계가 존재하는 범위 내에서는 이전 버전 패키지의 유형과 이전 패키지 인터페이스 하위 집합의 상속을 결합하여 표현할 수 있습니다.

인터페이스 구조화하기

구조화가 잘된 인터페이스의 경우 원본 설계에 포함되지 않은 기능의 새 유형을 추가하려면 HIDL 인터페이스를 수정해야 합니다. 반대로 인터페이스 자체를 변경하지 않으면서 인터페이스의 양측을 변경하여 새 기능을 도입할 수 있다면 인터페이스는 구조화되지 않습니다.

트레블은 기기의 vendor.imgsystem.img가 개별 컴파일될 수 있는 별도로 컴파일된 공급업체 및 시스템 구성요소를 지원합니다. vendor.imgsystem.img가 수년간 작동하려면 둘 사이의 모든 상호작용이 명시적으로 철저히 정의되어야 합니다. 여기에는 많은 API 노출 영역이 포함되지만 주요 노출 영역은 HIDL이 system.img/vendor.img 경계에서 프로세스 간 통신에 사용하는 IPC 메커니즘입니다.

요구사항

HIDL을 통해 전달되는 모든 데이터는 명시적으로 정의되어야 합니다. 별도로 컴파일하거나 개별적으로 개발하는 경우에도 구현과 클라이언트가 계속해서 함께 작동할 수 있으려면 데이터가 다음 요구사항을 준수해야 합니다.

  • 시맨틱 이름과 의미를 통해 HIDL로 직접 설명할 수 있습니다(구조체 enum 등을 사용).
  • ISO/IEC 7816과 같은 공개 표준으로 설명할 수 있습니다.
  • 하드웨어 표준 또는 하드웨어의 실제 레이아웃으로 설명할 수 있습니다.
  • 필요한 경우 불투명한 데이터(공개 키, ID 등)일 수 있습니다.

불투명한 데이터를 사용하는 경우 HIDL 인터페이스의 한쪽에서만 읽어야 합니다. 예를 들어 vendor.img 코드가 system.img의 구성요소에 문자열 메시지 또는 vec<uint8_t> 데이터를 제공하는 경우 이 데이터는 system.img 자체에서 파싱될 수 없으며 해석을 위해 vendor.img로 다시 전달되어야 합니다. vendor.img에서 system.img의 공급업체 코드 또는 다른 기기로 값을 전달할 때 데이터의 형식과 해석 방법을 정확하게 설명해야 하며 이는 여전히 인터페이스에 포함됩니다.

가이드라인

.hal 파일만 사용하여 HAL의 구현 또는 클라이언트를 작성할 수 있어야 합니다. 즉, Android 소스나 공개 표준을 참고할 필요가 없어야 합니다. 정확한 필수 동작을 지정하는 것이 좋습니다. '구현은 A 또는 B를 할 수 있습니다.'와 같은 문구는 구현이 함께 개발되는 클라이언트와 긴밀히 연관되도록 합니다.

HIDL 코드 레이아웃

HIDL에는 핵심 패키지와 공급업체 패키지가 포함됩니다.

핵심 HIDL 인터페이스는 Google에서 지정한 인터페이스입니다. 핵심 HIDL 인터페이스가 속한 패키지는 android.hardware.로 시작하고 하위 시스템에서 이름을 정하는데, 이름 지정 수준이 중첩되어 있을 수 있습니다. 예를 들어 NFC 패키지의 이름은 android.hardware.nfc이며 카메라 패키지는 android.hardware.camera입니다. 일반적으로 핵심 패키지의 이름은 android.hardware.[name1].[name2]…입니다. HIDL 패키지에는 이름 외에 버전이 있습니다. 예를 들어 패키지 android.hardware.camera는 버전 3.4에 있을 수 있습니다. 패키지의 버전이 소스 트리의 배치에 영향을 주므로 주의가 필요합니다.

모든 핵심 패키지는 빌드 시스템의 hardware/interfaces/ 아래에 배치됩니다. 버전 $m.$n의 패키지 android.hardware.[name1].[name2]…는 hardware/interfaces/name1/name2//$m.$n/ 아래에 있습니다. 패키지 android.hardware.camera 버전 3.4hardware/interfaces/camera/3.4/. 디렉터리에 있습니다. 패키지 프리픽스 android.hardware.와 경로 hardware/interfaces/ 사이에 하드 코딩 매핑이 존재합니다.

비핵심(공급업체) 패키지는 SoC 공급업체 또는 ODM에서 제작한 패키지입니다. 비핵심 패키지의 프리픽스는 vendor.$(VENDOR).hardware.이며 여기서 $(VENDOR)는 SoC 공급업체 또는 OEM/ODM을 가리킵니다. 이는 트리의 vendor/$(VENDOR)/interfaces 경로로 매핑되며 이 매핑 또한 하드 코딩됩니다.

정규화된 사용자 정의 유형 이름

HIDL의 경우 모든 UDT에는 UDT 이름, UDT가 정의된 패키지 이름, 패키지 버전으로 구성되는 정규화된 이름이 있습니다. 정규화된 이름은 유형의 인스턴스가 선언된 경우에만 사용되며 유형 자체가 정의된 경우에는 사용되지 않습니다. 예를 들어 패키지 android.hardware.nfc, 버전 1.0NfcData라는 구조체를 정의한다고 가정하겠습니다. types.hal 내부 또는 인터페이스의 선언 내부 등의 선언 장소에서 선언은 다음과 같이 간단히 명시합니다.

struct NfcData {
    vec<uint8_t> data;
};

데이터 구조 내부에 또는 메서드 매개변수로서 이 유형의 인스턴스를 선언할 때 다음과 같이 정규화된 유형 이름을 사용합니다.

android.hardware.nfc@1.0::NfcData

일반적인 구문은 PACKAGE@VERSION::UDT이며 각 부분의 의미는 다음과 같습니다.

  • PACKAGE는 HIDL 패키지의 점으로 구분된 이름입니다(예: android.hardware.nfc).
  • VERSION은 패키지의 점으로 구분된 주.부 버전 형식입니다(예: 1.0).
  • UDT는 HIDL UDT의 점으로 구분된 이름입니다. HIDL은 중첩된 UDT를 지원하고 HIDL 인터페이스는 중첩 선언의 한 형식인 UDT를 포함할 수 있으므로 이름에 액세스하는 데 점을 사용합니다.

예를 들어 패키지 android.hardware.example 버전 1.0의 공통 유형 파일에 다음의 중첩 선언이 정의되었다고 가정해 보겠습니다.

// types.hal
package android.hardware.example@1.0;
struct Foo {
    struct Bar {
        // …
    };
    Bar cheers;
};

Bar의 정규화된 이름은 android.hardware.example@1.0::Foo.Bar입니다. 중첩 선언이 위의 패키지뿐 아니라 IQuux라는 인터페이스에 존재한다고 가정해 보면 다음과 같습니다.

// IQuux.hal
package android.hardware.example@1.0;
interface IQuux {
    struct Foo {
        struct Bar {
            // …
        };
        Bar cheers;
    };
    doSomething(Foo f) generates (Foo.Bar fb);
};

Bar의 정규화된 이름은 android.hardware.example@1.0::IQuux.Foo.Bar입니다.

두 경우 모두 BarFoo의 선언 범위 내에서만 Bar로 지칭될 수 있습니다. 위의 메서드 doSomething 선언에서와 마찬가지로 패키지 또는 인터페이스 수준에서 Foo: Foo.Bar를 통해 Bar를 참조해야 합니다. 또는 다음과 같이 메서드를 더 상세하게 선언할 수 있습니다.

// IQuux.hal
doSomething(android.hardware.example@1.0::IQuux.Foo f) generates (android.hardware.example@1.0::IQuux.Foo.Bar fb);

정규화된 열거 값

UDT가 enum 유형인 경우 enum 유형의 각 값은 정규화된 이름을 가지며 이름은 enum 유형의 정규화된 이름으로 시작하여 뒤에 콜론, enum 값의 이름이 붙습니다. 예를 들어 패키지 android.hardware.nfc, 버전 1.0이 enum 유형 NfcStatus를 정의한다고 가정하겠습니다.

enum NfcStatus {
    STATUS_OK,
    STATUS_FAILED
};

STATUS_OK를 참조할 때 정규화된 이름은 다음과 같습니다.

android.hardware.nfc@1.0::NfcStatus:STATUS_OK

일반적인 구문은 PACKAGE@VERSION::UDT:VALUE이며 각 부분의 의미는 다음과 같습니다.

  • PACKAGE@VERSION::UDT는 enum 유형과 관련하여 완전히 동일한 정규화된 이름입니다.
  • VALUE는 값의 이름입니다.

자동 추론 규칙

정규화된 UDT 이름은 지정하지 않아도 됩니다. UDT 이름에서 다음은 생략해도 무방합니다.

  • 패키지(예: @1.0::IFoo.Type)
  • 패키지와 버전 모두(예: IFoo.Type)

HIDL은 자동 추론 규칙을 사용하여 이름을 완성하려고 시도합니다. 규칙 번호가 낮을수록 우선 순위가 더 높습니다.

규칙 1

패키지 및 버전이 제공되지 않으면 로컬 이름 조회를 시도합니다. 예:

interface Nfc {
    typedef string NfcErrorMessage;
    send(NfcData d) generates (@1.0::NfcStatus s, NfcErrorMessage m);
};

NfcErrorMessage는 로컬로 조회되며 그 위의 typedef가 발견됩니다. NfcData도 로컬에서 조회되지만 로컬에서 정의되지는 않으므로 규칙 2와 3이 사용됩니다. @1.0::NfcStatus는 버전을 제공하므로 규칙 1이 적용되지 않습니다.

규칙 2

규칙 1이 적용되지 않고 정규화된 이름의 구성요소가 누락된 경우(패키지, 버전 또는 패키지 및 버전) 구성요소는 현재 패키지의 정보로 자동 완성됩니다. 그러고 나면 HIDL 컴파일러는 자동 완성되고 정규화된 이름을 찾기 위해 현재 파일과 모든 가져오기를 살펴봅니다. 위 예를 사용하여 다음과 같이 ExtendedNfcData의 선언이 NfcData와 동일한 버전(1.0)의 동일한 패키지(android.hardware.nfc)에서 생성된 경우를 가정해 보겠습니다.

struct ExtendedNfcData {
    NfcData base;
    // … additional members
};

HIDL 컴파일러는 현재 패키지의 패키지 이름과 버전 이름을 작성하여 정규화된 UDT 이름 android.hardware.nfc@1.0::NfcData를 만듭니다. 가져오기를 제대로 했다고 가정한 상태에서 이름이 현재 패키지에 존재하므로 이 이름은 선언에 사용됩니다.

현재 패키지의 이름은 다음 중 하나에 적용되는 경우에만 가져옵니다.

  • import 문을 통해 명시적으로 가져왔습니다.
  • 현재 패키지의 types.hal에서 정의되었습니다.

버전 번호만으로 NfcData가 검증된 경우에도 동일한 프로세스가 적용됩니다.

struct ExtendedNfcData {
    // autofill the current package name (android.hardware.nfc)
    @1.0::NfcData base;
    // … additional members
};

규칙 3

규칙 2가 매칭을 생성하지 못하면(현재 패키지에 UDT가 정의되지 않음) HIDL 컴파일러는 가져온 모든 패키지 내에서 매칭을 검색합니다. 위 예를 사용하여 패키지 android.hardware.nfc의 버전 1.1에서 ExtendedNfcData가 선언되고 1.11.0을 제대로 가져오고(패키지 수준 확장 참조) 정의가 UDT 이름만 지정하는 경우를 가정해 보겠습니다.

struct ExtendedNfcData {
    NfcData base;
    // … additional members
};

컴파일러는 NfcData라는 UDT를 검색하여 1.0 버전의 android.hardware.nfc에서 하나를 찾아 android.hardware.nfc@1.0::NfcData의 정규화된 UDT가 검색됩니다. 지정된 부분 정규화된 UDT에 매칭이 두 개 이상 발견되면 HIDL 컴파일러에서 오류가 발생합니다.

규칙 2를 사용하면 현재 패키지에 정의된 가져온 유형이 다른 패키지의 가져온 유형보다 우선합니다.

// hardware/interfaces/foo/1.0/types.hal
package android.hardware.foo@1.0;
struct S {};

// hardware/interfaces/foo/1.0/IFooCallback.hal
package android.hardware.foo@1.0;
interface IFooCallback {};

// hardware/interfaces/bar/1.0/types.hal
package android.hardware.bar@1.0;
typedef string S;

// hardware/interfaces/bar/1.0/IFooCallback.hal
package android.hardware.bar@1.0;
interface IFooCallback {};

// hardware/interfaces/bar/1.0/IBar.hal
package android.hardware.bar@1.0;
import android.hardware.foo@1.0;
interface IBar {
    baz1(S s); // android.hardware.bar@1.0::S
    baz2(IFooCallback s); // android.hardware.foo@1.0::IFooCallback
};
  • Sandroid.hardware.bar@1.0::S로 보간되며 types.hal을 자동으로 가져오기 때문에 bar/1.0/types.hal에서 발견됩니다.
  • IFooCallback은 규칙 2를 사용해 android.hardware.bar@1.0::IFooCallback으로 보간되지만 types.hal과는 달리 bar/1.0/IFooCallback.hal을 자동으로 가져오지 않기 때문에 찾을 수 없습니다. 따라서 규칙 3에서 대신 android.hardware.foo@1.0::IFooCallback으로 결정하고 이는 import android.hardware.foo@1.0;을 통해 가져오기됩니다.

types.hal

모든 HIDL 패키지에는 패키지에 관여하는 모든 인터페이스 간에 공유되는 UDT를 포함하는 types.hal 파일이 있습니다. HIDL 유형은 항상 공개됩니다. UDT가 types.hal에서 또는 인터페이스 선언 내에서 선언되었는지 여부와 상관없이 HIDL 유형은 정의된 범위 외부에서 액세스할 수 있습니다. types.hal은 패키지의 공개 API를 설명하는 것이 아니라 패키지 내의 모든 인터페이스에서 사용하는 UDT를 호스팅하도록 되어 있습니다. HIDL 특성상 모든 UDT는 인터페이스에 포함됩니다.

types.hal은 UDT와 import 문으로 구성됩니다. types.hal은 패키지의 모든 인터페이스에서 사용할 수 있기 때문에(암시적 가져오기) import 문은 정의상 패키지 수준입니다. types.hal의 UDT는 UDT 및 가져온 인터페이스를 통합할 수도 있습니다.

예를 들면 IFoo.hal의 경우가 있습니다.

package android.hardware.foo@1.0;
// whole package import
import android.hardware.bar@1.0;
// types only import
import android.hardware.baz@1.0::types;
// partial imports
import android.hardware.qux@1.0::IQux.Quux;
// partial imports
import android.hardware.quuz@1.0::Quuz;

다음을 가져옵니다.

  • android.hidl.base@1.0::IBase(암시적)
  • android.hardware.foo@1.0::types(암시적)
  • android.hardware.bar@1.0의 모든 항목(모든 인터페이스 및 types.hal 포함)
  • android.hardware.baz@1.0::typestypes.hal(android.hardware.baz@1.0의 인터페이스는 가져오지 않음)
  • android.hardware.qux@1.0IQux.haltypes.hal
  • android.hardware.quuz@1.0Quuz(Quuztypes.hal에서 정의되었음을 가정할 때 types.hal 전체 파일이 파싱되며 Quuz 이외의 유형은 가져오지 않음)

인터페이스 수준 버전 관리

패키지 내의 각 인터페이스는 자체 파일에 있습니다. 인터페이스가 속한 패키지는 package 문을 사용하여 인터페이스의 상단에서 선언됩니다. 패키지 선언 다음에는 인터페이스 수준의 가져오기(부분 또는 전체 패키지)가 0개 이상 나열될 수 있습니다. 예:

package android.hardware.nfc@1.0;

HIDL에서 인터페이스는 extends 키워드를 사용하여 다른 인터페이스에서 상속받을 수 있습니다. 인터페이스가 다른 인터페이스를 확장하려면 import 문을 통해 해당 인터페이스에 액세스할 수 있어야 합니다. 확장되는 인터페이스(기본 인터페이스)의 이름은 위에 설명된 유형 이름 정규화에 관한 규칙을 따릅니다. 인터페이스는 하나의 인터페이스에서만 상속할 수 있습니다. HIDL은 다중 상속을 지원하지 않습니다.

아래의 업데이트 버전 관리 예시에서는 다음 패키지를 사용합니다.

// types.hal
package android.hardware.example@1.0
struct Foo {
    struct Bar {
        vec<uint32_t> val;
    };
};

// IQuux.hal
package android.hardware.example@1.0
interface IQuux {
    fromFooToBar(Foo f) generates (Foo.Bar b);
}

업데이트 규칙

패키지 package@major.minor를 정의하려면 A 또는 B의 모든 값이 참이여야 합니다.

규칙 A '시작 부 버전임': 모든 이전 부 버전, package@major.0, package@major.1, …, package@major.(minor-1)은 정의되어서는 안 됩니다.
또는
규칙 B

다음 모두에 해당:

  1. '이전 부 버전이 유효': package@major.(minor-1)이 정의되어야 하고 동일한 규칙 A(package@major.0부터 package@major.(minor-2)까지 아무것도 정의되지 않음) 또는 규칙 B(@major.(minor-2)에서의 업데이트인 경우)를 준수해야 합니다.

    그리고

  2. '이름이 동일한 인터페이스 최소 1개를 상속': package@major.(minor-1)::IFoo를 확장하는 인터페이스 package@major.minor::IFoo가 존재합니다(이전 패키지에 인터페이스가 있는 경우).

    그리고

  3. '이름이 다른 상속된 인터페이스가 없음': package@major.(minor-1)::IBaz를 확장하는 package@major.minor::IBar가 없어야 합니다. 여기서 IBarIBaz는 서로 다른 이름입니다. 이름이 동일한 인터페이스가 있다면 k 값이 더 작은 IBar가 없도록 package@major.minor::IBarpackage@major.(minor-k)::IBar를 확장해야 합니다.

규칙 A 때문에 다음 사항이 적용됩니다.

  • 패키지는 모든 부 버전 번호로 시작할 수 있습니다. 예를 들어 android.hardware.biometrics.fingerprint@2.1에서 시작합니다.
  • 요구사항 'android.hardware.foo@1.0은 정의되지 않음'은 디렉터리 hardware/interfaces/foo/1.0이 존재해서는 안 됨을 의미합니다.

하지만 규칙 A는 패키지 이름이 동일하지만 버전이 다른 패키지에 영향을 주지 않습니다. 예를 들어 android.hardware.camera.device@1.0@3.2를 정의하지만 @3.2@1.0과 상호작용할 필요가 없습니다. 따라서 @3.2::IExtFoo@1.0::IFoo를 확장할 수 있습니다.

패키지 이름이 다르다면 package@major.minor::IBar는 이름이 다른 인터페이스에서 확장될 수 있습니다. 예를 들어 android.hardware.bar@1.0::IBarandroid.hardware.baz@2.2::IBaz를 확장할 수 있습니다. 인터페이스가 extend 키워드로 슈퍼 유형을 명시적으로 선언하지 않는 경우 android.hidl.base@1.0::IBase를 확장하게 됩니다(IBase 자체는 제외).

B.2와 B.3은 동시에 준수되어야 합니다. 예를 들어 android.hardware.foo@1.1::IFooandroid.hardware.foo@1.0::IFoo를 확장하여 규칙 B.2를 통과한다 하더라도 android.hardware.foo@1.1::IExtBarandroid.hardware.foo@1.0::IBar를 확장한다면 유효한 업데이트가 아닙니다.

인터페이스 업데이트

위에서 정의된 android.hardware.example@1.0@1.1로 업데이트하려면 다음을 따릅니다.

// types.hal
package android.hardware.example@1.1;
import android.hardware.example@1.0;

// IQuux.hal
package android.hardware.example@1.1
interface IQuux extends @1.0::IQuux {
    fromBarToFoo(Foo.Bar b) generates (Foo f);
}

이것은 types.hal에 있는 android.hardware.example 버전 1.0의 패키지 수준 import입니다. 새 UDT가 패키지의 버전 1.1에 추가되지 않더라도 버전 1.0의 UDT에 관한 참조가 여전히 필요하기 때문에 types.hal에 패키지 수준 가져오기가 있습니다. IQuux.hal의 인터페이스 수준 가져오기를 사용하면 동일한 효과를 얻을 수 있습니다.

IQuux의 선언에 있는 extends @1.0::IQuux에서 상속 중인 IQuux의 버전을 지정했습니다. IQuux가 인터페이스를 선언하고 인터페이스에서 상속하는 데 사용되기 때문에 명확성이 필요합니다. 선언은 단순히 선언 장소에서 모든 패키지와 버전 속성을 상속하는 이름이기 때문에 기본 인터페이스의 이름에 명확성이 있어야 합니다. 정규화된 UDT도 사용할 수 있겠지만 이 경우 중복된다는 문제가 있습니다.

새 인터페이스 IQuux@1.0::IQuux에서 상속하는 메서드 fromFooToBar()를 다시 선언하지 않고 추가되는 새로운 메서드 fromBarToFoo()를 나열만 합니다. HIDL의 경우 상속된 메서드는 하위 인터페이스에서 다시 선언되지 않으므로 IQuux 인터페이스는 fromFooToBar() 메서드를 명시적으로 선언할 수 없습니다.

업데이트 규칙

인터페이스 이름이 확장 인터페이스의 이름을 변경해야 하는 경우도 있습니다. enum 확장, 구조체, 공용체는 새로운 이름이 필요할 정도로 다르지 않은 한 확장하는 것과 동일한 이름을 사용해야 합니다. 예:

// in parent hal file
enum Brightness : uint32_t { NONE, WHITE };

// in child hal file extending the existing set with additional similar values
enum Brightness : @1.0::Brightness { AUTOMATIC };

// extending the existing set with values that require a new, more descriptive name:
enum Color : @1.0::Brightness { HW_GREEN, RAINBOW };

메서드에 fooWithLocation과 같은 새로운 시맨틱 이름을 지정할 수 있는 경우 이런 이름이 선호됩니다. 그렇지 않으면 확장 대상과 유사한 이름을 지정해야 합니다. 예를 들어 더 나은 대체 이름이 없는 경우 @1.1::IFoo에 있는 메서드 foo_1_1@1.0::IFoo에 있는 foo 메서드의 기능을 대체할 수 있습니다.

패키지 수준 버전 관리

HIDL 버전 관리는 패키지 수준에서 진행됩니다. 패키지가 게시된 후에는 변경할 수 없습니다(인터페이스 및 UDT 집합 변경 불가). 패키지는 다양한 방법을 통해 서로 연관될 수 있으며 이 모두는 인터페이스 수준에서의 UDT 상속 및 빌드를 조합하여 표현 가능합니다.

하지만 패키지 수준의 하위 호환 가능 상속이라는 관계 유형은 엄격히 정의되고 적용되어야 합니다. 이 경우 상위 패키지는 상속해 주는 패키지이고 하위 패키지는 상위 패키지를 확장하는 패키지입니다. 패키지 수준의 하위 호환 가능한 상속 규칙은 다음과 같습니다.

  1. 상위 패키지의 모든 최상위 인터페이스는 하위 패키지의 인터페이스에서 상속합니다.
  2. 새로운 인터페이스도 새로운 패키지에 추가될 수 있습니다. 다른 패키지의 다른 인터페이스와의 관계에 관한 제한은 없습니다.
  3. 버전이 업데이트된 기존 인터페이스의 새 메서드나 새 인터페이스에서 사용할 수 있도록 새 데이터 유형을 추가할 수도 있습니다.

이러한 규칙은 HIDL 인터페이스 수준 상속과 UDT 조합을 사용하여 구현할 수 있지만 이러한 관계가 하위 호환 가능한 패키지 확장을 구성한다는 메타 수준의 지식이 필요합니다. 이 지식은 다음과 같이 추론됩니다.

패키지가 이 요구사항을 충족하는 경우 hidl-gen은 하위 호환성 규칙을 적용합니다.