ทุกอินเทอร์เฟซที่กำหนดไว้ในแพ็คเกจ 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>
ลูกค้าสามารถรับอินเทอร์เฟซได้สองวิธี:
- ผ่าน
I<InterfaceName>::getService
(ผ่านhwservicemanager
) - โดยวิธีอินเทอร์เฟซ
ไฟล์ส่วนหัวอินเทอร์เฟซที่สร้างขึ้นอัตโนมัติแต่ละไฟล์มีวิธี getService
แบบคงที่ที่สามารถใช้เพื่อดึงอินสแตนซ์บริการจาก hwservicemanager
:
// getService will return nullptr if the service can't be found sp<IFoo> myFoo = IFoo::getService(); sp<IFoo> myAlternateFoo = IFoo::getService("another_foo");
ขณะนี้ไคลเอ็นต์มีอินเทอร์เฟ IFoo
และสามารถเรียกใช้เมธอดต่างๆ ได้เหมือนกับว่าเป็นการใช้งานคลาสในเครื่อง ในความเป็นจริง การใช้งานอาจทำงานในกระบวนการเดียวกัน กระบวนการอื่น หรือแม้แต่บนอุปกรณ์อื่น (ด้วยรีโมท HAL) เนื่องจากไคลเอนต์เรียก getService
บนอ็อบเจ็กต์ IFoo
ที่รวมมาจากเวอร์ชัน 1.0
ของแพ็คเกจ hwservicemanager
จึงส่งคืนการใช้งานเซิร์ฟเวอร์เฉพาะในกรณีที่การใช้งานนั้นเข้ากันได้กับไคลเอนต์ 1.0
ในทางปฏิบัติ หมายถึงเฉพาะการใช้งานเซิร์ฟเวอร์ที่มีเวอร์ชัน 1.n
(เวอร์ชัน x.(y+1)
ของอินเทอร์เฟซเท่านั้นที่ต้องขยาย (สืบทอดจาก) xy
)
นอกจากนี้ ยังมีเมธอด 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);
การโทรกลับแบบอะซิงโครนัส
การใช้งาน HAL ที่มีอยู่จำนวนมากพูดคุยกับฮาร์ดแวร์แบบอะซิงโครนัส ซึ่งหมายความว่าพวกเขาต้องการวิธีแบบอะซิงโครนัสเพื่อแจ้งให้ลูกค้าทราบถึงเหตุการณ์ใหม่ที่เกิดขึ้น อินเทอร์เฟซ HIDL สามารถใช้เป็นการโทรกลับแบบอะซิงโครนัสได้ เนื่องจากฟังก์ชันอินเทอร์เฟซ 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()
จะส่งกลับค่าเท็จ) วิธีเดียวที่จะกู้คืนจากความล้มเหลวดังกล่าวได้คือการขออินสแตนซ์ใหม่ของบริการโดยการเรียก 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);