| <html devsite><head> |
| <title>创建 HAL 接口</title> |
| <meta name="project_path" value="/_project.yaml"/> |
| <meta name="book_path" value="/_book.yaml"/> |
| </head> |
| <body> |
| <!-- |
| Copyright 2017 The Android Open Source Project |
| |
| Licensed under the Apache License, Version 2.0 (the "License"); |
| you may not use this file except in compliance with the License. |
| You may obtain a copy of the License at |
| |
| http://www.apache.org/licenses/LICENSE-2.0 |
| |
| Unless required by applicable law or agreed to in writing, software |
| distributed under the License is distributed on an "AS IS" BASIS, |
| WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| See the License for the specific language governing permissions and |
| limitations under the License. |
| --> |
| |
| <p>您必须使用 HIDL 来描述用于对框架进行条件式编译的所有编译标记。相关编译标记必须分组并包含在单个 <code>.hal</code> 文件中。使用 HIDL 指定配置项具有以下优势:</p> |
| <ul> |
| <li>可实施版本控制(为了添加新配置项,供应商/OEM 必须明确扩展 HAL)</li> |
| <li>记录详尽</li> |
| <li>可使用 SELinux 实现访问控制</li> |
| <li>可通过<a href="/devices/tech/test_infra/tradefed/fundamentals/vts">供应商测试套件</a>对配置项进行全面检查(范围检查、各项内容之间的相互依赖性检查等)</li> |
| <li>在 C++ 和 Java 中自动生成 API</li> |
| </ul> |
| |
| <h2 id="identify-flags">确定框架使用的编译标记</h2> |
| <p>首先,请确定用于对框架进行条件式编译的编译标记,然后舍弃过时的配置以缩小编译标记集的范围。例如,下列编译标记集已确定用于 <code>surfaceflinger</code>:</p> |
| <ul> |
| <li><code>TARGET_USES_HWC2</code>(即将弃用)</li> |
| <li><code>TARGET_BOARD_PLATFORM</code></li> |
| <li><code>TARGET_DISABLE_TRIPLE_BUFFERING</code></li> |
| <li><code>TARGET_FORCE_HWC_FOR_VIRTUAL_DISPLAYS</code></li> |
| <li><code>NUM_FRAMEBUFFER_SURFACE_BUFFERS</code></li> |
| <li><code>TARGET_RUNNING_WITHOUT_SYNC_FRAMEWORK</code></li> |
| <li><code>VSYNC_EVENT_PHASE_OFFSET_NS</code></li> |
| <li><code>SF_VSYNC_EVENT_PHASE_OFFSET_NS</code>(即将弃用)</li> |
| <li><code>PRESENT_TIME_OFFSET_FROM_VSYNC_NS</code></li> |
| <li><code>MAX_VIRTUAL_DISPLAY_DIMENSION</code></li> |
| </ul> |
| |
| <h2 id="create-interface">创建 HAL 接口</h2> |
| <p>子系统的编译配置是通过 HAL 接口访问的,而用于提供配置值的接口会在 HAL 软件包 <code>android.hardware.configstore</code>(目前为 1.0 版)中进行分组。例如,要为 <code>surfaceflinger</code> 创建 HAL 接口文件,请在 <strong><code>hardware/interfaces/configstore/1.0/ISurfaceFlingerConfigs.hal</code></strong> 中运行以下命令: |
| </p> |
| |
| <pre class="devsite-click-to-copy"> |
| package [email protected]; |
| |
| interface ISurfaceFlingerConfigs { |
| // TO-BE-FILLED-BELOW |
| }; |
| </pre> |
| |
| <p>创建 <code>.hal</code> 文件后,请运行 <code>hardware/interfaces/update-makefiles.sh</code> 以将新的 <code>.hal</code> 文件添加到 <code>Android.bp</code> 和 <code>Android.mk</code> 文件中。</p> |
| |
| <h2 id="add-functions">为编译标记添加函数</h2> |
| <p>对于每个编译标记,请向相应接口各添加一个新函数。例如,在 <strong><code>hardware/interfaces/configstore/1.0/ISurfaceFlingerConfigs.hal</code></strong> 中运行以下命令: |
| </p> |
| |
| <pre class="devsite-click-to-copy"> |
| interface ISurfaceFlingerConfigs { |
| disableTripleBuffering() generates(OptionalBool ret); |
| forceHwcForVirtualDisplays() generates(OptionalBool ret); |
| enum NumBuffers: uint8_t { |
| USE_DEFAULT = 0, |
| TWO = 2, |
| THREE = 3, |
| }; |
| numFramebufferSurfaceBuffers() generates(NumBuffers ret); |
| runWithoutSyncFramework() generates(OptionalBool ret); |
| vsyncEventPhaseOffsetNs generates (OptionalUInt64 ret); |
| presentTimeOffsetFromSyncNs generates (OptionalUInt64 ret); |
| maxVirtualDisplayDimension() generates(OptionalInt32 ret); |
| }; |
| </pre> |
| |
| <p>添加函数时,请注意以下事项:</p> |
| <ul> |
| <li><strong>采用简洁的名称</strong>。请避免将 makefile 变量名称转换为函数名称,并切记 <code>TARGET_</code> 和 <code>BOARD_</code> 前缀不再是必需的。</li> |
| <li><strong>添加注释</strong>。帮助开发者了解配置项的用途,配置项如何改变框架行为、有效值等。</li> |
| </ul> |
| <p>函数返回类型可以是 <code>Optional[Bool|String|Int32|UInt32|Int64|UInt64]</code>。类型会在同一目录中的 <code>types.hal</code> 中进行定义,并使用字段(可表明原始值是否是由 HAL 指定)来封装原始值;如果原始值不是由 HAL 指定,则使用默认值。</p> |
| |
| <pre class="devsite-click-to-copy"> |
| struct OptionalString { |
| bool specified; |
| string value; |
| }; |
| </pre> |
| |
| <p>在适当的情况下,请定义最能代表配置项类型的枚举,并将该枚举用作返回类型。在上述示例中,<code>NumBuffers</code> 枚举会被定义为限制有效值的数量。在定义这类自定义数据类型时,请添加字段或枚举值(例如 <code>USE_DEFAULT</code>)来表示该值是否由 HAL 指定。</p> |
| |
| <p>在 HIDL 中,单个编译标记并不一定要变成单个函数。模块所有者也可以将密切相关的编译标记汇总为一个结构体,并通过某个函数返回该结构体(这样做可以减少函数调用的次数)。</p> |
| |
| <p>例如,用于在 <strong><code>hardware/interfaces/configstore/1.0/ISurfaceFlingerConfigs.hal</code></strong> 中将两个编译标记汇总到单个结构体的选项如下:</p> |
| |
| <pre class="devsite-click-to-copy"> |
| interface ISurfaceFlingerConfigs { |
| // other functions here |
| struct SyncConfigs { |
| OptionalInt64 vsyncEventPhaseoffsetNs; |
| OptionalInt64 presentTimeoffsetFromSyncNs; |
| }; |
| getSyncConfigs() generates (SyncConfigs ret); |
| // other functions here |
| }; |
| </pre> |
| |
| <h2 id="alternatives">单个 HAL 函数的替代函数</h2> |
| |
| <p>作为针对所有编译标记使用单个 HAL 函数的替代函数,HAL 接口还提供了 <code>getBoolean(string |
| key)</code> 和 <code>getInteger(string key)</code> 等简单函数。实际的 <code>key=value</code> 对会存储在单独的文件中,而 HAL 服务会通过读取/解析这些文件来提供值。</p> |
| |
| <p>虽然这种方法很容易定义,但它不具备 HIDL 提供的优势(强制实施版本控制、便于记录、实现访问控制),因此不推荐使用。</p> |
| |
| <p class="note"><strong>注意</strong>:在使用简单函数时,几乎不可能实现访问控制,因为 HAL 自身无法识别客户端。</p> |
| |
| <h2 id="single-multiple">单个接口与多个接口</h2> |
| <p>面向配置项设计的 HAL 接口提供了以下两种选择:</p> |
| |
| <ol> |
| <li>单个接口;涵盖所有配置项</li> |
| <li>多个接口;每个接口分别涵盖一组相关配置项</li> |
| </ol> |
| <p>单个接口更易于使用,但随着更多的配置项添加到单个文件中,单个接口可能会越来越难以维护。此外,由于访问控制不够精细,获得接口访问权限的进程可能会读取所有配置项(无法授予对部分配置项的访问权限)。此外,如果未授予访问权限,则无法读取任何配置项。</p> |
| |
| <p>由于存在这些问题,Android 会针对一组相关配置项将多个接口与单个 HAL 接口搭配使用。例如,对 <code>surfaceflinger</code> 相关配置项使用 <code>ISurfaceflingerConfigs</code>,对蓝牙相关配置项使用 <code>IBluetoothConfigs</code> 等等。</p> |
| |
| </body></html> |