| <html devsite><head> |
| <title>数据类型</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 数据声明可生成 C++ 标准布局数据结构。您可以将这些结构放置在任何合适的位置(可以放在堆栈上,放在文件或全局范围内,也可以放在堆区上),而且这些结构能以相同的方式构成。客户端代码会调用传入常量引用和基元类型的 HIDL 代理代码,而存根和代理代码会隐藏序列化的细节。</p> |
| |
| <p class="note"><strong>注意</strong>:在任何情况下,开发者编写的代码都不需要对数据结构进行显式序列化或反序列化。</p> |
| |
| <p>下表说明 HIDL 基元与 C++ 数据类型之间的对应关系:</p> |
| |
| <table> |
| <tbody> |
| <tr> |
| <th><strong>HIDL 类型</strong></th> |
| <th><strong>C++ 类型</strong></th> |
| <th><strong>头文件/库</strong></th> |
| </tr> |
| |
| <tr> |
| <td><pre> |
| |
| enum</pre></td> |
| <td><pre> |
| |
| enum class</pre></td> |
| <td></td> |
| </tr> |
| |
| <tr> |
| <td><pre> |
| |
| uint8_t..uint64_t</pre></td> |
| <td><pre> |
| |
| uint8_t..uint64_t</pre></td> |
| <td><pre> |
| |
| <stdint.h></pre></td> |
| </tr> |
| |
| <tr> |
| <td><pre> |
| |
| int8_t..int64_t</pre></td> |
| <td><pre> |
| |
| int8_t..int64_t</pre></td> |
| <td><pre> |
| |
| <stdint.h></pre></td> |
| </tr> |
| |
| <tr> |
| <td><pre> |
| |
| float</pre></td> |
| <td><pre> |
| |
| float</pre></td> |
| <td></td> |
| </tr> |
| |
| <tr> |
| <td><pre> |
| |
| double</pre></td> |
| <td><pre> |
| |
| double</pre></td> |
| <td></td> |
| </tr> |
| |
| <tr> |
| <td><pre> |
| |
| vec<T></pre></td> |
| <td><pre> |
| |
| hidl_vec<T></pre></td> |
| <td><pre> |
| |
| libhidlbase</pre></td> |
| </tr> |
| |
| <tr> |
| <td><pre> |
| |
| T[S1][S2]...[SN]</pre></td> |
| <td><pre> |
| |
| T[S1][S2]...[SN]</pre></td> |
| <td></td> |
| </tr> |
| |
| <tr> |
| <td><pre> |
| |
| string</pre></td> |
| <td><pre> |
| |
| hidl_string</pre></td> |
| <td><pre> |
| |
| libhidlbase</pre></td> |
| </tr> |
| |
| <tr> |
| <td><pre> |
| |
| handle</pre></td> |
| <td><pre> |
| |
| hidl_handle</pre></td> |
| <td><pre> |
| |
| libhidlbase</pre></td> |
| </tr> |
| |
| <tr> |
| <td><pre> |
| |
| opaque</pre></td> |
| <td><pre> |
| |
| uint64_t</pre></td> |
| <td><pre> |
| |
| <stdint.h></pre> |
| </td> |
| </tr> |
| |
| <tr> |
| <td><pre> |
| |
| struct</pre></td> |
| <td><pre> |
| |
| struct</pre></td> |
| <td></td> |
| </tr> |
| |
| <tr> |
| <td><pre> |
| |
| union</pre></td> |
| <td><pre> |
| |
| union</pre></td> |
| <td></td> |
| </tr> |
| |
| <tr> |
| <td><pre> |
| |
| fmq_sync</pre></td> |
| <td><pre> |
| |
| MQDescriptorSync</pre></td> |
| <td><pre> |
| |
| libhidlbase</pre></td> |
| </tr> |
| |
| <tr> |
| <td><pre> |
| |
| fmq_unsync</pre></td> |
| <td><pre> |
| |
| MQDescriptorUnsync</pre></td> |
| <td><pre> |
| |
| libhidlbase</pre></td> |
| </tr> |
| |
| </tbody> |
| </table> |
| |
| <p>下面几部分详细介绍了这些数据类型。</p> |
| |
| <h2 id="enum">枚举</h2> |
| <p>HIDL 形式的枚举会变成 C++ 形式的枚举。例如:</p> |
| <pre class="prettyprint"> |
| enum Mode : uint8_t { WRITE = 1 << 0, READ = 1 << 1 }; |
| </pre> |
| |
| <p>…会变为:</p> |
| <pre class="prettyprint"> |
| enum class Mode : uint8_t { WRITE = 1, READ = 2 }; |
| </pre> |
| |
| <h2 id="bitfield">bitfield<T></h2> |
| <p><code>bitfield<T></code>(其中 <code>T</code> 是用户定义的枚举)会变为 C++ 形式的该枚举的底层类型。在上述示例中,<code>bitfield<Mode></code> 会变为 <code>uint8_t</code>。</p> |
| |
| <h2 id="vect">vec<T></h2> |
| <p><code>hidl_vec<T></code> 类模板是 <code>libhidlbase</code> 的一部分,可用于传递具备任意大小的任何 HIDL 类型的矢量。与之相当的具有固定大小的容器是 <code>hidl_array</code>。此外,您也可以使用 <code>hidl_vec::setToExternal()</code> 函数将 <code>hidl_vec<T></code> 初始化为指向 <code>T</code> 类型的外部数据缓冲区。</p> |
| |
| <p>除了在生成的 C++ 头文件中适当地发出/插入结构之外,您还可以使用 <code>vec<T></code> 生成一些便利函数,用于转换到 <code>std::vector</code> 和 <code>T</code> 裸指针或从它们进行转换。如果您将 <code>vec<T></code> 用作参数,则使用它的函数将过载(将生成两个原型),以接受并传递该参数的 HIDL 结构和 <code>std::vector<T></code> 类型。</p> |
| |
| <h2 id="arrays">数组</h2> |
| <p>hidl 中的常量数组由 <code>libhidlbase</code> 中的 <code>hidl_array</code> 类表示。<code>hidl_array<T, S1, S2, …, |
| SN></code> 表示具有固定大小的 N 维数组 <code>T[S1][S2]…[SN]</code>。</p> |
| |
| <h2 id="string">字符串</h2> |
| <p><code>hidl_string</code> 类(<code>libhidlbase</code> 的一部分)可用于通过 HIDL 接口传递字符串,并在 <code>/system/libhidl/base/include/hidl/HidlSupport.h</code> 下进行定义。该类中的第一个存储位置是指向其字符缓冲区的指针。</p> |
| |
| <p><code>hidl_string</code> 知道如何使用 <code>operator=</code>、隐式类型转换和 <code>.c_str()</code> 函数转换自或转换到 <code>std::string and char*</code>(C 样式的字符串)。HIDL 字符串结构具有适当的复制构造函数和赋值运算符,可用于:</p> |
| |
| <ul> |
| <li>从 <code>std::string</code> 或 C 字符串加载 HIDL 字符串。</li> |
| <li>从 HIDL 字符串创建新的 <code>std::string</code>。</li> |
| </ul> |
| |
| <p>此外,HIDL 字符串还有转换构造函数,因此 C 字符串 (<code>char *</code>) 和 C++ 字符串 (<code>std::string</code>) 可用于采用 HIDL 字符串的方法。</p> |
| |
| <h2 id="struct">结构</h2> |
| <p>HIDL 形式的 <code>struct</code> 只能包含固定大小的数据类型,不能包含任何函数。HIDL 结构定义会直接映射到 C++ 形式的标准布局 <code>struct</code>,从而确保 <code>struct</code> 具有一致的内存布局。一个结构可以包括多种指向单独的可变长度缓冲区的 HIDL 类型(包括 <code>handle</code>、<code>string</code> 和 <code>vec<T></code>)。</p> |
| |
| <h2 id="handle">句柄</h2> |
| |
| <p class="warning"><strong>警告</strong>:任何类型的地址(即使是物理设备地址)都不得是原生句柄的一部分。在进程之间传递该信息很危险,会导致进程容易受到攻击。在进程之间传递的任何值都必须先经过验证,然后才能用于在进程内查找分配的内存。否则,错误的句柄可能会导致内存访问错误或内存损坏。</p> |
| |
| <p><code>handle</code> 类型由 C++ 形式的 <code>hidl_handle</code> 结构表示,该结构是一个简单的封装容器,用于封装指向 <code>const native_handle_t</code> 对象的指针(该对象已经在 Android 中存在了很长时间)。</p> |
| |
| <pre> |
| typedef struct native_handle |
| { |
| int version; /* sizeof(native_handle_t) */ |
| int numFds; /* number of file descriptors at &data[0] */ |
| int numInts; /* number of ints at &data[numFds] */ |
| int data[0]; /* numFds + numInts ints */ |
| } native_handle_t; |
| </pre> |
| |
| <p>默认情况下,<code>hidl_handle</code> 并<strong>不</strong>具备对它所封装的 <code>native_handle_t</code> 指针的所有权。它的存在只是为了安全地存储指向 <code>native_handle_t</code> 的指针,以使其在 32 位和 64 位进程中均可使用。</p> |
| |
| <p>在以下情况下,<code>hidl_handle</code> 会拥有对其所封装文件描述符的所有权:</p> |
| <ul> |
| <li>在调用 <code>setTo(native_handle_t* handle, bool |
| shouldOwn)</code> 方法(<code>shouldOwn</code> 参数已设置为 <code>true</code>)后</li> |
| <li>当通过复制其他 <code>hidl_handle</code> 对象的构造创建 <code>hidl_handle</code> 对象时</li> |
| <li>当 <code>hidl_handle</code> 对象从其他 <code>hidl_handle</code> 对象复制赋值时</li> |
| </ul> |
| |
| <p><code>hidl_handle</code> 可提供到/从 <code>native_handle_t* </code>对象的隐式和显式转换。在 HIDL 中,<code>handle</code> 类型的主要用途是通过 HIDL 接口传递文件描述符。因此,单个文件描述符由没有 <code>int</code> 的 <code>native_handle_t</code> 和单个 <code>fd</code> 表示。如果客户端和服务器在不同的进程中运行,则 RPC 实现将自动处理文件描述符,以确保这两个进程可对同一个文件执行操作。</p> |
| |
| <p>尽管由某个进程在 <code>hidl_handle</code> 中接收的文件描述符在该进程中有效,但超出接收函数范围后该描述符将不会持续存在(该函数返回后描述符将会关闭)。要持续访问文件描述符,进程必须对所封装的文件描述符执行 <code>dup()</code> 操作或复制整个 <code>hidl_handle</code> 对象。 |
| </p> |
| |
| <h2 id="memory">内存</h2> |
| <p>HIDL <code>memory</code> 类型会映射到 <code>libhidlbase</code> 中的 <code>hidl_memory</code> 类,该类表示未映射的共享内存。这是要在 HIDL 中共享内存而必须在进程之间传递的对象。要使用共享内存,需满足以下条件:</p> |
| |
| <ol> |
| <li>获取 <code>IAllocator</code> 的实例(当前只有“ashmem”实例可用)并使用该实例分配共享内存。</li> |
| <li><code>IAllocator::allocate()</code> 返回 <code>hidl_memory</code> 对象,该对象可通过 HIDL RPC 传递,并能使用 <code>libhidlmemory</code> 的 <code>mapMemory</code> 函数映射到某个进程。</li> |
| <li><code>mapMemory</code> 返回对可用于访问内存的 <code>sp<IMemory></code> 对象的引用(<code>IMemory</code> 和 <code>IAllocator</code> 在 <code>android.hidl.memory@1.0</code> 中定义)。</li> |
| </ol> |
| |
| <p><code>IAllocator</code> 的实例可用于分配内存:</p> |
| <pre class="prettyprint"> |
| #include <android/hidl/allocator/1.0/IAllocator.h> |
| #include <android/hidl/memory/1.0/IMemory.h> |
| #include <hidlmemory/mapping.h> |
| using ::android::hidl::allocator::V1_0::IAllocator; |
| using ::android::hidl::memory::V1_0::IMemory; |
| using ::android::hardware::hidl_memory; |
| .... |
| sp<IAllocator> ashmemAllocator = IAllocator::getService("ashmem"); |
| ashmemAllocator->allocate(2048, [&](bool success, const hidl_memory& mem) { |
| if (!success) { /* error */ } |
| // now you can use the hidl_memory object 'mem' or pass it around |
| })); |
| </pre> |
| |
| <p>对内存的实际更改必须通过 <code>IMemory</code> 对象完成(在创建 <code>mem</code> 的一端或在通过 HIDL RPC 接收更改的一端完成)。</p> |
| |
| <pre class="prettyprint"> |
| // Same includes as above |
| |
| sp<IMemory> memory = mapMemory(mem); |
| void* data = memory->getPointer(); |
| memory->update(); |
| // update memory however you wish after calling update and before calling commit |
| data[0] = 42; |
| memory->commit(); |
| // … |
| memory->update(); // the same memory can be updated multiple times |
| // … |
| memory->commit(); |
| </pre> |
| |
| <h2 id="interfaces">接口</h2> |
| <p>接口可作为对象传递。“接口”一词可用作 <code>android.hidl.base@1.0::IBase</code> 类型的语法糖;此外,当前的接口以及任何导入的接口都将定义为一个类型。<em></em></p> |
| |
| <p>存储接口的变量应该是强指针:<code>sp<IName></code>。接受接口参数的 HIDL 函数会将原始指针转换为强指针,从而导致不可预料的行为(可能会意外清除指针)。为避免出现问题,请务必将 HIDL 接口存储为 <code>sp<></code>。</p> |
| |
| </body></html> |