Android HIDL基础篇(一)

前言

这一系列文章基本都以英文原版的形式表现出来。

会补充一些自己在学习的过程中遇到的不懂的地方加上自己的一些见解。

官方文档(要梯子):https://source.android.com/devices/architecture/hidl

HIDL简介

HAL interface definition language or HIDL (pronounced “hide-l”) is an interface description language (IDL) to specify the interface between a HAL and its users. It allows specifying types and method calls, collected into interfaces and packages. More broadly, HIDL is a system for communicating between codebases that may be compiled independently.HIDL is intended to be used for inter-process communication (IPC). Communication between processes is referred to as Binderized. For libraries that must be linked to a process, a passthough mode is also available (not supported in Java).HIDL specifies data structures and method signatures, organized in interfaces (similar to a class) that are collected into packages. The syntax of HIDL will look familiar to C++ and Java programmers, though with a different set of keywords. HIDL also uses Java-style annotations.

补充:

HAL(Hardware Abstract Layer),是Google开发的Android系统里上层应用对底层硬件操作屏蔽一个软件层次,也就是硬件独立,Android系统不依赖于某一个具体的硬件驱动,而是依赖于HAL代码。通俗点就是上层的应用不用关心底层硬件具体如何工作的,只要向上层提供一个统一的接口即可。

官方简介大致表明了这么几个点:

  1. HIDL是HAL接口定义语言,用来描述指定HAL与用户间的接口。
  2. HIDL也可以定义为能被独立编译的代码库之间的通信系统。
  3. HIDL用于进程间通信,进程间通信可称为Binderized(binder机制传送门)。
  4. HIDL可指定数据结构和方法签名组织成接口从而汇集到软件包中。

HIDL设计

The goal of HIDL is that the framework can be replaced without having to rebuild HALs. HALs will be built by vendors or SOC makers and put in a /vendor partition on the device, enabling the framework, in its own partition, to be replaced with an OTA without recompiling the HALs.

HIDL design balances the following concerns:

  • Interoperability. Create reliably interoperable interfaces between processes which may be compiled with various architectures, toolchains, and build configurations. HIDL interfaces are versioned and cannot be changed after they are published.
  • Efficiency. HIDL tries to minimize the number of copy operations. HIDL-defined data is delivered to C++ code in C++ standard layout data structures that can be used without unpacking. HIDL also provides shared memory interfaces and, as RPCs are inherently somewhat slow, HIDL supports two ways to transfer data without using an RPC call: shared memory and a Fast Message Queue (FMQ).
  • Intuitive. HIDL avoids thorny issues of memory ownership by using only in parameters for RPC (see Android Interface Definition Language (AIDL)); values that cannot be efficiently returned from methods are returned via callback functions. Neither passing data into HIDL for transfer nor receiving data from HIDL changes the ownership of the data—ownership always remains with the calling function. Data needs to persist only for the duration of the called function and may be destroyed immediately after the called function returns.

也就是说HIDL的设计需要从Interoperability(互操作性)、Efficiency(效率性)和Intuitive(直观性)三个方面互相平衡。至于怎么平衡,取决于需求以及设计者的水平。

HIDL语法

语法这部分在写代码的时候需要注意,但是目前处于了解基础的情况下可以不做深入理解。以下仅供参考

By design, the HIDL language is similar to C (but does not use the C preprocessor). All punctuation not described below (aside from the obvious use of = and |) is part of the grammar.

Note: For details on HIDL code style, see the Code Style Guide.

  • // indicates a documentation comment.
  • /* */ indicates a multiline comment.
  • // indicates a comment to end of line. Aside from //, newlines are the same as any other whitespace.
  • In the example grammar below, text from // to the end of the line is not part of the grammar but is instead a comment on the grammar.
  • [empty] means that the term may be empty.
  • ? following a literal or term means it is optional.
  • … indicates sequence containing zero or more items with separating punctuation as indicated. There are no variadic arguments in HIDL.
  • Commas separate sequence elements.
  • Semicolons terminate each element, including the last element.
  • UPPERCASE is a nonterminal.
  • italics is a token family such as integer or identifier (standard C parsing rules).
  • constexpr is a C style constant expression (such as 1 + 1 and 1L << 3).
  • import_name is a package or interface name, qualified as described in HIDL Versioning.
  • Lowercase words are literal tokens.

eg.

ROOT =
    PACKAGE IMPORTS PREAMBLE { ITEM ITEM ... }  // not for types.hal
    PREAMBLE = interface identifier EXTENDS
  | PACKAGE IMPORTS ITEM ITEM...  // only for types.hal; no method definitions

ITEM =
    ANNOTATIONS? oneway? identifier(FIELD, FIELD ...) GENERATES?;
  |  struct identifier { SFIELD; SFIELD; ...};  // Note - no forward declarations
  |  union identifier { UFIELD; UFIELD; ...};
  |  enum identifier: TYPE { ENUM_ENTRY, ENUM_ENTRY ... }; // TYPE = enum or scalar
  |  typedef TYPE identifier;

VERSION = integer.integer;

PACKAGE = package android.hardware.identifier[.identifier[...]]@VERSION;

PREAMBLE = interface identifier EXTENDS

EXTENDS = <empty> | extends import_name  // must be interface, not package

GENERATES = generates (FIELD, FIELD ...)

// allows the Binder interface to be used as a type
// (similar to typedef'ing the final identifier)
IMPORTS =
   [empty]
  |  IMPORTS import import_name;

TYPE =
  uint8_t | int8_t | uint16_t | int16_t | uint32_t | int32_t | uint64_t | int64_t |
 float | double | bool | string
|  identifier  // must be defined as a typedef, struct, union, enum or import
               // including those defined later in the file
|  memory
|  pointer
|  vec<TYPE>
|  bitfield<TYPE>  // TYPE is user-defined enum
|  fmq_sync<TYPE>
|  fmq_unsync<TYPE>
|  TYPE[SIZE]

FIELD =
   TYPE identifier

UFIELD =
   TYPE identifier
  |  struct identifier { FIELD; FIELD; ...} identifier;
  |  union identifier { FIELD; FIELD; ...} identifier;

SFIELD =
   TYPE identifier
  |  struct identifier { FIELD; FIELD; ...};
  |  union identifier { FIELD; FIELD; ...};
  |  struct identifier { FIELD; FIELD; ...} identifier;
  |  union identifier { FIELD; FIELD; ...} identifier;

SIZE =  // Must be greater than zero
     constexpr

ANNOTATIONS =
     [empty]
  |  ANNOTATIONS ANNOTATION

ANNOTATION =
  |  @identifier
  |  @identifier(VALUE)
  |  @identifier(ANNO_ENTRY, ANNO_ENTRY  ...)

ANNO_ENTRY =
     identifier=VALUE

VALUE =
     "any text including \" and other escapes"
  |  constexpr
  |  {VALUE, VALUE ...}  // only in annotations

ENUM_ENTRY =
     identifier
  |  identifier = constexpr

HIDL--包

HIDL is built around interfaces, an abstract type used in object-oriented languages to define behaviors. Each interface is part of a package.Package names can have sublevels such as package.subpackage. The root directory for published HIDL packages is hardware/interfaces or vendor/vendorName (e.g. vendor/google for Pixel devices). The package name forms one or more subdirectories under the root directory; all files defining a package are in the same directory. For example, package android.hardware.example.extension.light@2.0 could be found under hardware/interfaces/example/extension/light/2.0.The package directory contains files with extension .hal. Every file must contain a package statement naming the package and version the file is part of. The file types.hal, if present, does not define an interface but instead defines data types accessible to every interface in the package.

  • HIDL是HAL定义接口的语言,接口属于包。
  • HIDL包的根目录在hardware/interfaces 或者 vendor/vendorName上。
  • 根据包的命名方式可以在根目录下形成其子目录,也就是包的名字和目录有着对应的关系。eg.如果存在下面这样子的包名android.hardware.example.extension.light@2.0,就一定会存在这样子的文件夹hardware/interfaces/example/extension/light/2.0。
  • 在包目录下的.hal文件都必须包含对包的名称和版本的声明。如果存在types.hal文件,它只是定义包中每个接口都可以访问的数据类型。

HIDL--接口描述

Aside from types.hal, every other .hal file defines an interface. For example, an interface is typically defined as follows:

interface IBar extends IFoo { // IFoo is another interface
    // embedded types
    struct MyStruct {/*...*/};

    // interface methods
    create(int32_t id) generates (MyStruct s);
    close();
};

An interface without an explicit extends declaration implicitly extends from android.hidl.base@1.0::IBase (similar to java.lang.Object in Java.) The IBase interface, implicitly imported, declares several reserved methods that should not and cannot be redeclared in user-defined interfaces or used otherwise. These methods include:

  • ping
  • interfaceChain
  • interfaceDescriptor
  • notifySyspropsChanged
  • linkToDeath
  • unlinkToDeath
  • setHALInstrumentation
  • getDebugInfo
  • debug
  • getHashChain

除了types.hal文件外,其他的.hal文件都被定义为接口。

上面描述的方法不能被重定义,无论显式还是隐式继承。

HIDL--导入

The import statement is HIDL mechanism to access package interfaces and types in another package. An import statement concerns itself with two entities:

  • The importing entity, which can be either a package or an interface;
  • The imported entity, which too can be either a package or an interface.

The importing entity is determined by the location of the import statement. When the statement is inside a package’s types.hal, what is being imported is visible by the entire package; this is a package-level import. When the statement is inside an interface file, the importing entity is the interface itself; this is an interface-level import.

根据不同的导入声明的位置,可以分为包级别的导入和接口级别的导入。特别的是当导入types.hal时也是包级别的。

The imported entity is determined by the value after the import keyword. The value need not be a fully-qualified name; if a component is omitted, it is automatically filled with information from the current package. For fully-qualified values, the following import cases are supported:

  • Whole-package imports. If the value is a package name and a version (syntax described below), then the entire package is imported into the importing entity.
  • Partial imports
    • If the value is an interface, the package’s types.hal and that interface are imported into the importing entity.
    • If the value is an UDT defined in types.hal, then only that UDT is imported into the importing entity (other types in types.hal are not imported).
  • Types-only imports. If the value uses the syntax of a partial import described above, but with the keyword types instead of an Interface name, only the UDTs in types.hal of the designated package are imported.

根据导入的keyword,可以分为全包导入,局部导入和类型导入。

The importing entity gets access to a combination of:

  • The imported package’s common UDTs defined in types.hal;
  • The imported package’s interfaces (for a whole-package import) or specified interface (for a partial import) for the purposes of invoking them, passing handles to them and/or inheriting from them.

 导入的实体可以访问包中types.hal中通用的UDTs,可以访问所有接口或者部分接口。

The import statement uses the fully-qualified-type-name syntax to provide the name and version of the package or interface being imported:

  • import android.hardware.nfc@1.0; // import a whole package
  • import android.hardware.example@1.0::IQuux; // import an interface and types.hal
  • import android.hardware.example@1.0::types; // import just types.hal

 HIDL--接口继承

An interface can be an extension of a previously-defined interface. Extensions can be one of the following three types:

  • Interface can add functionality to another one, incorporating its API unchanged.
  • Package can add functionality to another one, incorporating its API unchanged.
  • Interface can import types from a package or from a specific interface.

 An interface can extend only one other interface (no multiple inheritance). Each interface in a package with a non-zero minor version number must extend an interface in the previous version of the package. For example, if an interface IBar in version 4.0 of package derivative is based on (extends) an interface IFoo in version 1.2 of package original, and a version 1.3 of package original is created, IBar version 4.1 cannot extend version 1.3 of IFoo. Instead, IBar version 4.1 must extend IBar version 4.0, which is tied to IFoo version 1.2. IBar version 5.0 could extend IFoo version 1.3, if desired.

Interface extensions do not imply library dependence or cross-HAL inclusion in the generated code—they simply import the data structure and method definitions at the HIDL level. Every method in a HAL must be implemented in that HAL.

一个接口可以是一个预先定义接口的扩展。扩展有三种类型,接口对接口扩展功能,包对包扩展功能,接口导入包或者特定接口的类型。一个接口只能扩展一个其他接口,不支持多重继承。

HIDL接口哈希

This document describes HIDL interface hashing, a mechanism to prevent accidental interface changes and ensure interface changes are thoroughly vetted. This mechanism is required because HIDL interfaces are versioned, which means that after an interface is released it must not be changed except in an Application Binary Interface (ABI) preserving manner (such as a comment correction).

描述了接口哈希这个机制的目的和必要性。防止接口被意外被修改,除了特定的方式,任何已经发布的接口都不可以被改变。

Every package root directory (i.e. android.hardware mapping to hardware/interfaces or vendor.foo mapping to vendor/foo/hardware/interfaces) must contain a current.txt file that lists all released HIDL interface files.

每个包根目录下必须包含一个具有全部已发布接口的current.txt文件。

You can add a hash to a current.txt file manually or by using hidl-gen. The following code snippet provides examples of commands you can use with hidl-gen to manage a current.txt file (hashes have been shortened):

$ hidl-gen -L hash -r vendor.awesome:vendor/awesome/hardware/interfaces -r android.hardware:hardware/interfaces -r android.hidl:system/libhidl/transport vendor.awesome.nfc@1.0::types
9626fd18...f9d298a6 vendor.awesome.nfc@1.0::types

Warning: Do not replace a hash for a previously-released interface. When changing such an interface, add a new hash to the end of the current.txt file. For details, refer to ABI stability.

Every interface definition library generated by hidl-gen includes hashes, which can be retrieved by calling IBase::getHashChain. When hidl-gen is compiling an interface, it checks the current.txt file in the root directory of the HAL package to see if the HAL has been changed:

  • If no hash for the HAL is found, the interface is considered unreleased (in development) and compilation proceeds.
  • If hashes are found, they are checked against the current interface: 
    • If the interface does match the hash, compilation proceeds.
    • If the interface does not match a hash, compilation is halted as this means a previously-released interface is being changed. 
      • For an ABI-preserving change (see ABI stability), the current.txt file must be modified before compilation can proceed.
      • All other changes should be made in a minor or major version upgrade of the interface.

 哈希值可以自己手动添加和hidl-gen自动生成。

ABI stability

An Application Binary Interface (ABI) includes the binary linkages/calling conventions/etc. If the ABI/API changes, the interface no longer works with a generic system.img that was compiled with official interfaces.

为了保证官方编译的system.img正常工作,需要保证ABI稳定。也就是说没得商量,必须保证ABI稳定。

Making sure that interfaces are versioned and ABI stable is crucial for several reasons:

  • It ensures your implementation can pass the Vendor Test Suite (VTS), which puts you on track to being able to do framework-only OTAs.
  • As an OEM, it enables you to provide a Board Support Package (BSP) that is straightforward to use and compliant.
  • It helps you keep track of what interfaces can be released. Consider current.txt a map of an interfaces directory that allows you to see the history and state of all interfaces being provided in a package root.

确保接口版本化和ABI稳定的原因有:通过出版商测试套件的测试,对框架进行升级;提供一个板级支持包使用;可以跟踪接口。

When adding a new hash for an interface that already has an entry in current.txt, make sure to add only the hashes that represent interfaces which maintain ABI stability. Review the following types of changes:

  • Changes allowed 
    • Changing a comment (unless this changes the meaning of a method).
    • Changing the name of a parameter.
    • Changing the name of a return parameter.
    • Changing annotations.
  • Changes not allowed 
    • Reordering arguments, methods, etc…
    • Adding a method/struct field/etc… anywhere in the interface.
    • Anything that would break a C++ vtable.
    • etc..

为已经存在的接口添加一个新的哈希值时,需要保证ABI稳定。允许被改变的和不允许被改变的如上。

 

Android HIDL基础篇(二)

  • 0
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值