อินเทอร์เฟซทุกรายการที่กําหนดไว้ในแพ็กเกจ HIDL จะมีคลาส C++ ที่สร้างขึ้นโดยอัตโนมัติของตัวเองภายในเนมสเปซของแพ็กเกจ ไคลเอ็นต์และเซิร์ฟเวอร์จัดการอินเทอร์เฟซด้วยวิธีที่แตกต่างกัน ดังนี้
- เซิร์ฟเวอร์ใช้อินเทอร์เฟซ
- ไคลเอ็นต์เรียกเมธอดในอินเทอร์เฟซ
เซิร์ฟเวอร์สามารถลงทะเบียนอินเทอร์เฟซตามชื่อหรือส่งเป็นพารามิเตอร์ไปยังเมธอดที่ HIDL กำหนดก็ได้ เช่น โค้ดเฟรมเวิร์กสามารถแสดงอินเทอร์เฟซเพื่อรับข้อความแบบไม่พร้อมกันจาก HAL และส่งอินเทอร์เฟซนั้นไปยัง HAL ได้โดยตรงโดยไม่ต้องลงทะเบียน
การติดตั้งใช้งานเซิร์ฟเวอร์
เซิร์ฟเวอร์ที่ใช้อินเทอร์เฟซ IFoo
ต้องมีไฟล์ส่วนหัว IFoo
ที่สร้างขึ้นโดยอัตโนมัติ
#include <android/hardware/samples/1.0/IFoo.h>
ไลบรารีที่แชร์ของอินเทอร์เฟซ IFoo
จะส่งออกส่วนหัวโดยอัตโนมัติเพื่อลิงก์ ตัวอย่าง IFoo.hal
// IFoo.hal interface IFoo { someMethod() generates (vec<uint32_t>); ... }
ตัวอย่างโครงสําหรับการติดตั้งใช้งานเซิร์ฟเวอร์ของอินเทอร์เฟซ IFoo
// 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(); } ... };
หากต้องการให้การติดตั้งใช้งานอินเทอร์เฟซเซิร์ฟเวอร์พร้อมใช้งานสำหรับไคลเอ็นต์ คุณจะทำสิ่งต่อไปนี้ได้
- ลงทะเบียนการติดตั้งใช้งานอินเทอร์เฟซด้วย
hwservicemanager
(ดูรายละเอียดด้านล่าง)
หรือ
- ส่งการใช้งานอินเทอร์เฟซเป็นอาร์กิวเมนต์ของเมธอดอินเทอร์เฟซ (ดูรายละเอียดที่การเรียกกลับแบบแอซิงโครนัส)
เมื่อลงทะเบียนการใช้งานอินเทอร์เฟซ hwservicemanager
กระบวนการจะติดตามอินเทอร์เฟซ HIDL ที่ลงทะเบียนไว้ซึ่งทำงานบนอุปกรณ์ตามชื่อและเวอร์ชัน เซิร์ฟเวอร์สามารถลงทะเบียนการใช้งานอินเทอร์เฟซ HIDL ตามชื่อ และลูกค้าสามารถขอการใช้งานบริการตามชื่อและเวอร์ชัน กระบวนการนี้จะแสดงอินเทอร์เฟซ HIDL
android.hidl.manager@1.0::IServiceManager
ไฟล์ส่วนหัวอินเทอร์เฟซ HIDL ที่สร้างขึ้นโดยอัตโนมัติแต่ละไฟล์ (เช่น IFoo.h
) จะมีเมธอด registerAsService()
ที่ใช้เพื่อลงทะเบียนการใช้งานอินเทอร์เฟซกับ hwservicemanager
ได้ อาร์กิวเมนต์ที่จําเป็นเพียงอย่างเดียวคือชื่อของการใช้งานอินเทอร์เฟซ เนื่องจากไคลเอ็นต์จะใช้ชื่อนี้เพื่อดึงข้อมูลอินเทอร์เฟซจาก hwservicemanager
ในภายหลัง
::android::sp<IFoo> myFoo = new FooImpl(); ::android::sp<IFoo> mySecondFoo = new FooAnotherImpl(); status_t status = myFoo->registerAsService(); status_t anotherStatus = mySecondFoo->registerAsService("another_foo");
hwservicemanager
จะถือว่าการรวม [package@version::interface, instance_name]
นั้นไม่ซ้ำกันเพื่อให้อินเทอร์เฟซต่างๆ (หรืออินเทอร์เฟซเดียวกันในเวอร์ชันต่างๆ) ลงทะเบียนด้วยชื่ออินสแตนซ์ที่เหมือนกันได้โดยไม่เกิดข้อขัดแย้ง หากคุณเรียกใช้ registerAsService()
ด้วยเวอร์ชันแพ็กเกจ อินเทอร์เฟซ และชื่ออินสแตนซ์เดียวกันทุกประการ hwservicemanager
จะยกเลิกการอ้างอิงบริการที่ลงทะเบียนไว้ก่อนหน้านี้และใช้บริการใหม่
การติดตั้งใช้งานไคลเอ็นต์
ไคลเอ็นต์ต้อง#include
อินเทอร์เฟซทุกรายการ
ที่อ้างอิงถึง เช่นเดียวกับเซิร์ฟเวอร์
#include <android/hardware/samples/1.0/IFoo.h>
ลูกค้ารับอินเทอร์เฟซได้ 2 วิธีดังนี้
- ผ่าน
I<InterfaceName>::getService
(ผ่านhwservicemanager
) - ผ่านเมธอดอินเทอร์เฟซ
ไฟล์ส่วนหัวอินเทอร์เฟซที่สร้างขึ้นโดยอัตโนมัติแต่ละไฟล์มีgetService
วิธีแบบคงที่ซึ่งสามารถใช้ดึงข้อมูลอินสแตนซ์บริการจากhwservicemanager
ได้ ดังนี้
// getService returns nullptr if the service can't be found sp<IFoo> myFoo = IFoo::getService(); sp<IFoo> myAlternateFoo = IFoo::getService("another_foo");
ตอนนี้ไคลเอ็นต์มีอินเทอร์เฟซ IFoo
และสามารถเรียกเมธอดเพื่อดำเนินการกับอินเทอร์เฟซนั้นได้ราวกับเป็นการใช้งานคลาสภายใน ในทางปฏิบัติ การติดตั้งใช้งานสามารถทํางานในกระบวนการเดียวกัน กระบวนการอื่น หรือแม้แต่ในอุปกรณ์เครื่องอื่น (ด้วย HAL remoting) เนื่องจากไคลเอ็นต์เรียก getService
บนออบเจ็กต์ IFoo
ที่รวมอยู่ในแพ็กเกจเวอร์ชัน 1.0
hwservicemanager
จึงแสดงผลการใช้งานเซิร์ฟเวอร์เฉพาะในกรณีที่การใช้งานนั้นเข้ากันได้กับไคลเอ็นต์ 1.0
ในทางปฏิบัติ หมายความว่ามีเพียงการใช้งานเซิร์ฟเวอร์ที่มีเวอร์ชัน 1.n
(เวอร์ชัน x.(y+1)
ของอินเทอร์เฟซต้องขยาย (รับค่ามาจาก) x.y
)
นอกจากนี้ ยังมีเมธอด castFrom
สำหรับแคสต์ระหว่างอินเทอร์เฟซต่างๆ วิธีนี้ทํางานโดยการเรียก IPC ไปยังอินเทอร์เฟซระยะไกลเพื่อให้แน่ใจว่าประเภทพื้นฐานเหมือนกับประเภทที่ขอ หากประเภทที่ขอไม่พร้อมใช้งาน ระบบจะแสดง nullptr
sp<V1_0::IFoo> foo1_0 = V1_0::IFoo::getService(); sp<V1_1::IFoo> foo1_1 = V1_1::IFoo::castFrom(foo1_0);
Callback แบบอะซิงโครนัส
การใช้งาน HAL ที่มีอยู่จำนวนมากจะสื่อสารกับฮาร์ดแวร์แบบไม่พร้อมกัน ซึ่งหมายความว่าต้องใช้วิธีแบบไม่พร้อมกันเพื่อแจ้งเหตุการณ์ใหม่ๆ ที่เกิดขึ้นกับไคลเอ็นต์ อินเทอร์เฟซ HIDL สามารถใช้เป็น Callback แบบไม่พร้อมกันได้ เนื่องจากฟังก์ชันอินเทอร์เฟซ HIDL สามารถใช้ออบเจ็กต์อินเทอร์เฟซ HIDL เป็นพารามิเตอร์ได้
ตัวอย่างไฟล์อินเทอร์เฟซ IFooCallback.hal
package android.hardware.samples@1.0; interface IFooCallback { sendEvent(uint32_t event_id); sendData(vec<uint8_t> data); }
ตัวอย่างเมธอดใหม่ใน IFoo
ที่ใช้พารามิเตอร์
IFooCallback
package android.hardware.samples@1.0; interface IFoo { struct Foo { int64_t someValue; handle myHandle; }; someMethod(Foo foo) generates (int32_t ret); anotherMethod() generates (vec<uint32_t>); registerCallback(IFooCallback callback); };
ไคลเอ็นต์ที่ใช้อินเทอร์เฟซ IFoo
คือเซิร์ฟเวอร์ของอินเทอร์เฟซ IFooCallback
ซึ่งให้บริการติดตั้งใช้งาน IFooCallback
ดังนี้
class FooCallback : public IFooCallback { Return<void> sendEvent(uint32_t event_id) { // process the event from the HAL } Return<void> sendData(const hidl_vec<uint8_t>& data) { // process data from the HAL } };
หรือจะส่งผ่านข้อมูลดังกล่าวไปยังอินสแตนซ์ที่มีอยู่ของIFoo
อินเทอร์เฟซก็ได้เช่นกัน โดยทำดังนี้
sp<IFooCallback> myFooCallback = new FooCallback(); myFoo.registerCallback(myFooCallback);
เซิร์ฟเวอร์ที่ใช้ IFoo
จะรับค่านี้ในรูปแบบออบเจ็กต์ sp<IFooCallback>
โดยสามารถจัดเก็บการเรียกกลับและเรียกกลับไปยังไคลเอ็นต์ได้ทุกเมื่อที่ต้องการใช้อินเทอร์เฟซนี้
ผู้รับการเสียชีวิต
เนื่องจากการติดตั้งใช้งานบริการสามารถทํางานในกระบวนการอื่นได้ จึงอาจเป็นไปได้ที่กระบวนการติดตั้งใช้งานอินเทอร์เฟซจะหยุดทํางานขณะที่ไคลเอ็นต์ยังคงทํางานอยู่
การเรียกใช้ออบเจ็กต์อินเทอร์เฟซที่โฮสต์ในกระบวนการที่หยุดทำงานจะดำเนินการไม่สำเร็จพร้อมข้อผิดพลาดในการขนส่ง (isOK()
แสดงผลเป็น false
) วิธีเดียวที่จะแก้ไขข้อผิดพลาดดังกล่าวได้คือขออินสแตนซ์ใหม่ของบริการโดยเรียกใช้ I<InterfaceName>::getService()
ซึ่งจะใช้งานได้ก็ต่อเมื่อกระบวนการที่ขัดข้องได้เริ่มทำงานอีกครั้งและลงทะเบียนบริการกับ servicemanager
อีกครั้ง (ซึ่งโดยทั่วไปจะเป็นจริงสำหรับการใช้งาน HAL)
แทนที่จะจัดการกับปัญหานี้แบบตอบสนองต่อเหตุการณ์ ลูกค้าของอินเทอร์เฟซยังลงทะเบียนผู้รับการแจ้งเตือนการหยุดให้บริการเพื่อรับการแจ้งเตือนเมื่อบริการหยุดให้บริการได้ด้วย
หากต้องการลงทะเบียนรับการแจ้งเตือนดังกล่าวในอินเทอร์เฟซ IFoo
ที่ดึงข้อมูลมา ลูกค้าจะทำดังนี้ได้
foo->linkToDeath(recipient, 1481 /* cookie */);
พารามิเตอร์ recipient
ต้องเป็นการใช้งานอินเทอร์เฟซ android::hardware::hidl_death_recipient
ที่ HIDL ให้มา ซึ่งมีเมธอด serviceDied()
รายการเดียวที่เรียกใช้จากเธรดในพูลเธรด RPC เมื่อกระบวนการที่โฮสต์อินเทอร์เฟซสิ้นสุดลง
class MyDeathRecipient : public 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 } }
พารามิเตอร์ cookie
มีคุกกี้ที่ส่งมากับ linkToDeath()
ส่วนพารามิเตอร์ who
มีพอยน์เตอร์แบบอ่อนไปยังออบเจ็กต์ที่แสดงถึงบริการในไคลเอ็นต์ เมื่อใช้การเรียกตัวอย่างข้างต้น cookie
จะเท่ากับ 1481 และ who
จะเท่ากับ foo
นอกจากนี้ คุณยังยกเลิกการลงทะเบียนผู้รับผลประโยชน์ในกรณีที่เสียชีวิตได้หลังจากลงทะเบียนแล้ว โดยทำดังนี้
foo->unlinkToDeath(recipient);