| <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++ 类。客户端和服务器会通过不同的方式处理接口:</p> |
| |
| <ul> |
| <li><strong>服务器</strong>实现接口。</li> |
| <li><strong>客户端</strong>在接口上调用方法。</li> |
| </ul> |
| |
| <p>接口可以由服务器按名称注册,也可以作为参数传递到以 HIDL 定义的方法。例如,框架代码可设定一个从 HAL 接收异步消息的接口,并将该接口直接传递到 HAL(无需注册该接口)。</p> |
| |
| <h2 id="server">服务器实现</h2> |
| <p>实现 <code>IFoo</code> 接口的服务器必须包含自动生成的 <code>IFoo</code> 头文件:</p> |
| |
| <pre class="prettyprint"> |
| #include <android/hardware/samples/1.0/IFoo.h> |
| </pre> |
| |
| <p>该标头由 <code>IFoo</code> 接口的共享库自动导出以进行链接。<code>IFoo.hal</code> 示例:</p> |
| |
| <pre class="prettyprint"> |
| // IFoo.hal |
| interface IFoo { |
| someMethod() generates (vec<uint32_t>); |
| ... |
| } |
| </pre> |
| |
| <p>IFoo 接口的服务器实现的示例框架:</p> |
| |
| <pre class="prettyprint"> |
| // From the IFoo.h header |
| using android::hardware::samples::V1_0::IFoo; |
| |
| class FooImpl : public IFoo { |
| Return<void> someMethod(foo my_foo, someMethod_cb _cb) { |
| vec<uint32_t> return_data; |
| // Compute return_data |
| _cb(return_data); |
| return Void(); |
| } |
| ... |
| }; |
| </pre> |
| |
| <p>要使服务器接口的实现可供客户端使用,您可以:</p> |
| |
| <ol> |
| <li>向 <code>hwservicemanager</code> <strong>注册</strong>接口实现(详情见下文), |
| <br /><br />或<br /><br /> |
| </li> |
| <li>将接口实现作为接口方法的参数进行<strong>传递</strong>(详情见<a href="#asynchronous">异步回调</a>)。</li> |
| </ol> |
| |
| <p>注册接口实现时,<code>hwservicemanager</code> 进程会按名称和版本号跟踪设备上正在运行的已注册 HIDL 接口。服务器可以按名称注册 HIDL 接口实现,而客户端则可以按名称和版本号请求服务实现。该进程可提供 HIDL 接口 <code>[email protected]::IServiceManager</code>。</p> |
| |
| <p>每个自动生成的 HIDL 接口头文件(如 <code>IFoo.h</code>)都有一个 <code>registerAsService()</code> 方法,可用于向 <code>hwservicemanager</code> 注册接口实现。唯一一个必需的参数是接口实现的名称,因为稍后客户端将使用此名称从 <code>hwservicemanager</code> 检索接口:</p> |
| |
| <pre class="prettyprint"> |
| ::android::sp<IFoo> myFoo = new FooImpl(); |
| ::android::sp<IFoo> mySecondFoo = new FooAnotherImpl(); |
| status_t status = myFoo->registerAsService(); |
| status_t anotherStatus = mySecondFoo->registerAsService("another_foo"); |
| </pre> |
| |
| <p><code>hwservicemanager</code> 会将 <code>[package@version::interface, instance_name]</code> 组合视为唯一,以使不同的接口(或同一接口的不同版本)能够采用完全相同的实例名称无冲突地注册。如果您调用的 <code>registerAsService()</code> 具有完全相同的软件包版本、接口和实例名称,则 <code>hwservicemanager</code> 将丢弃对先前注册的服务的引用,并使用新的服务。</p> |
| |
| <h2 id="client">客户端实现</h2> |
| <p>和服务器一样,客户端也必须 <code>#include</code> 其引用的每个接口:</p> |
| |
| <pre class="prettyprint"> |
| #include <android/hardware/samples/1.0/IFoo.h> |
| </pre> |
| |
| <p>客户端可以通过两种方式获取接口:</p> |
| |
| <ul> |
| <li>通过 <code>I<InterfaceName>::getService</code>(借助 <code>hwservicemanager</code>)</li> |
| <li>通过接口方法</li> |
| </ul> |
| |
| <p>每个自动生成的接口头文件都有一个静态 <code>getService</code> 方法,可用于从 <code>hwservicemanager</code> 检索服务实例:</p> |
| |
| <pre class="prettyprint"> |
| // getService will return nullptr if the service can't be found |
| sp<IFoo> myFoo = IFoo::getService(); |
| sp<IFoo> myAlternateFoo = IFoo::getService("another_foo"); |
| </pre> |
| |
| <p>现在,客户端有一个 <code>IFoo</code> 接口,并可以向其(将其当作本地类实现)调用方法。实际上,实现可以在同一个进程中运行,也可以在不同的进程中运行,甚至还可以在另一个设备上运行(通过 HAL 远程处理)。由于客户端在 <code>1.0 </code> 版软件包中包含的 <code>IFoo</code> 对象上调用 <code>getService</code>,因此仅当服务器实现与 <code>1.0</code> 客户端兼容时,<code>hwservicemanager</code> 才会返回该实现。实际上,这意味着系统只会返回版本为 <code>1.n</code> 的服务器实现(<code>x.(y+1)</code> 版本的接口必须扩展(继承自)<code>x.y</code>)。</p> |
| |
| <p>此外,您也可以使用 <code>castFrom</code> 方法,在不同的接口之间进行类型转换。该方法会通过以下方式发挥作用:对远程接口进行 IPC 调用,以确保底层类型与正在请求的类型相同。如果请求的类型不可用,则返回 <code>nullptr</code>。</p> |
| |
| <pre class="prettyprint"> |
| sp<V1_0::IFoo> foo1_0 = V1_0::IFoo::getService(); |
| sp<V1_1::IFoo> foo1_1 = V1_1::IFoo::castFrom(foo1_0); |
| </pre> |
| |
| <h2 id="asynchronous">异步回调</h2> |
| <p>很多现有的 HAL 实现会与异步硬件通信,这意味着它们需要以异步方式通知客户端已发生的新事件。HIDL 接口可以用作异步回调,因为 HIDL 接口函数可以将 HIDL 接口对象用作参数。</p> |
| |
| <p><code>IFooCallback.hal</code> 接口文件示例:</p> |
| |
| <pre class="prettyprint"> |
| package [email protected]; |
| interface IFooCallback { |
| sendEvent(uint32_t event_id); |
| sendData(hidl_vec<uint8_t> data); |
| } |
| </pre> |
| |
| <p><code>IFoo</code> 中采用 <code>IFooCallback</code> 参数的新方法示例:</p> |
| |
| <pre class="prettyprint"> |
| package [email protected]; |
| interface IFoo { |
| struct Foo { |
| int64_t some_value; |
| Handle my_handle; |
| }; |
| |
| someMethod(Foo foo) generates (int32_t ret); |
| another_method() generates (hidl_vec<uint32_t>); |
| register_callback(IFooCallback callback); |
| }; |
| </pre> |
| |
| <p>使用 <code>IFoo</code> 接口的客户端是 <code>IFooCallback</code> 接口的服务器;它会提供 <code>IFooCallback</code> 的实现:<em></em><em></em></p> |
| |
| <pre class="prettyprint"> |
| class FooCallback : public IFooCallback { |
| Return<void> sendEvent(uint32_t event_id) { |
| // process the event from the HAL |
| } |
| Return<void> sendData(hidl_vec<uint8_t> data) { |
| // process data from the HAL |
| } |
| }; |
| </pre> |
| |
| <p>它也可以简单地通过 <code>IFoo</code> 接口的现有实例来传递该实现:</p> |
| <pre class="prettyprint"> |
| sp<IFooCallback> myFooCallback = new FooCallback(); |
| myFoo.registerCallback(myFooCallback); |
| </pre> |
| |
| <p>实现 <code>IFoo</code> 的服务器会将此作为 <code>sp<IFooCallback></code> 对象进行接收。它可以存储该回调,而且只要它想使用此接口,均可回调到客户端。</p> |
| |
| <h2 id="death">服务终止通知接收方</h2> |
| <p>由于服务实现可以在不同的进程中运行,因此可能会出现实现接口的进程已终止但客户端仍保持活动状态的情况。对托管在已终止进程中的接口对象的任何调用都将失败,并会返回相应的传输错误(<code>isOK()</code> 将返回 false)。要从这类故障中恢复正常,唯一的方法是通过调用 <code>I<InterfaceName>::getService()</code> 来请求服务的新实例。仅当崩溃的进程已重新启动且已向 <code>servicemanager</code> 重新注册其服务时,这种方法才有效(对 HAL 实现而言通常如此)。 |
| </p> |
| |
| <p>接口的客户端也可以注册为服务终止通知接收方,以便在服务终止时收到通知,而不是被动地应对这种情况。<em></em>要在检索的 <code>IFoo</code> 接口上注册此类通知,客户端可以执行以下操作:</p> |
| |
| <pre class="prettyprint"> |
| foo->linkToDeath(recipient, 1481 /* cookie */); |
| </pre> |
| |
| <p><code>recipient</code> 参数必须是由 HIDL 提供的 <code>android::hardware::hidl_death_recipient</code> 接口的实现,该接口中包含在托管接口的进程终止时从 RPC 线程池中的线程调用的单个 <code>serviceDied()</code> 方法:</p> |
| |
| <pre class="prettyprint"> |
| class MyDeathRecipient : android::hardware::hidl_death_recipient { |
| virtual void serviceDied(uint64_t cookie, const android::wp<::android::hidl::base::V1_0::IBase>& who) { |
| // Deal with the fact that the service died |
| } |
| } |
| </pre> |
| |
| <p><code>cookie</code> 参数包含通过 <code>linkToDeath()</code> 传入的 Cookie,而 <code>who</code> 参数则包含一个弱指针,它指向表示客户端中的服务的对象。在上面给出的调用示例中,<code>cookie</code> 等于 1481,<code>who</code> 等于 <code>foo</code>。</p> |
| |
| <p>您也可以在注册服务终止通知接收方后将其取消注册:</p> |
| |
| <pre class="prettyprint"> |
| foo->unlinkToDeath(recipient); |
| </pre> |
| |
| </body></html> |