blob: e5b13047a43d07edab390e841929625a12c7bc9f [file] [log] [blame]
<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>
&lt;stdint.h&gt;</pre></td>
</tr>
<tr>
<td><pre>
int8_t..int64_t</pre></td>
<td><pre>
int8_t..int64_t</pre></td>
<td><pre>
&lt;stdint.h&gt;</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&lt;T&gt;</pre></td>
<td><pre>
hidl_vec&lt;T&gt;</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>
&lt;stdint.h&gt;</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 &lt;&lt; 0, READ = 1 &lt;&lt; 1 };
</pre>
<p>会变成:</p>
<pre class="prettyprint">
enum class Mode : uint8_t { WRITE = 1, READ = 2 };
</pre>
<h2 id="bitfield">bitfield&lt;T&gt;</h2>
<p><code>bitfield&lt;T&gt;</code>(其中 <code>T</code> 是用户定义的枚举)会变为 C++ 形式的该枚举的底层类型。在上述示例中,<code>bitfield&lt;Mode&gt;</code> 会变为 <code>uint8_t</code></p>
<h2 id="vect">vec&lt;T&gt;</h2>
<p><code>hidl_vec&lt;T&gt;</code> 类模板是 <code>libhidlbase</code> 的一部分,可用于传递具备任意大小的任何 HIDL 类型的矢量。与之相当的具有固定大小的容器是 <code>hidl_array</code>。此外,您也可以使用 <code>hidl_vec::setToExternal()</code> 函数将 <code>hidl_vec&lt;T&gt;</code> 初始化为指向 <code>T</code> 类型的外部数据缓冲区。</p>
<p>除了在生成的 C++ 标头中适当地发出/插入结构之外,您还可以使用 <code>vec&lt;T&gt;</code> 生成一些便利函数,用于转换到 <code>std::vector</code><code>T</code> 裸指针或从它们进行转换。如果您将 <code>vec&lt;T&gt;</code> 用作参数,则使用它的函数将过载(将生成两个原型),以接受并传递该参数的 HIDL 结构和 <code>std::vector&lt;T&gt;</code> 类型。</p>
<h2 id="arrays">数组</h2>
<p>hidl 中的常量数组由 <code>libhidlbase</code> 中的 <code>hidl_array</code> 类表示。<code>hidl_array&lt;T, S1, S2, …,
SN&gt;</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&lt;T&gt;</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 &amp;data[0] */
int numInts; /* number of ints at &amp;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&lt;IMemory&gt;</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 &lt;android/hidl/allocator/1.0/IAllocator.h&gt;
#include &lt;android/hidl/memory/1.0/IMemory.h&gt;
#include &lt;hidlmemory/mapping.h&gt;
using ::android::hidl::allocator::V1_0::IAllocator;
using ::android::hidl::memory::V1_0::IMemory;
using ::android::hardware::hidl_memory;
....
sp&lt;IAllocator&gt; ashmemAllocator = IAllocator::getService("ashmem");
ashmemAllocator-&gt;allocate(2048, [&amp;](bool success, const hidl_memory&amp; 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&lt;IMemory&gt; memory = mapMemory(mem);
void* data = memory-&gt;getPointer();
memory-&gt;update();
// update memory however you wish after calling update and before calling commit
data[0] = 42;
memory-&gt;commit();
// …
memory-&gt;update(); // the same memory can be updated multiple times
// …
memory-&gt;commit();
</pre>
<h2 id="interfaces">接口</h2>
<p>接口可作为对象传递。“接口”一词可用作 <code>android.hidl.base@1.0::IBase</code> 类型的语法糖衣;此外,当前的接口以及任何导入的接口都将定义为一种类型。<em></em></p>
<p>存储接口的变量应该是强指针:<code>sp&lt;IName&gt;</code>。接受接口参数的 HIDL 函数会将原始指针转换为强指针,从而导致非直觉行为(可能会意外清除指针)。为避免出现问题,请务必将 HIDL 接口存储为 <code>sp&lt;&gt;</code></p>
</body></html>