หน้านี้อธิบายวิธีลงทะเบียนและค้นหาบริการ รวมถึงวิธีส่งข้อมูลไปยังบริการโดยการเรียกใช้เมธอดที่กําหนดไว้ในอินเทอร์เฟซในไฟล์ .hal
ลงทะเบียนบริการ
เซิร์ฟเวอร์อินเทอร์เฟซ HIDL (ออบเจ็กต์ที่ใช้อินเทอร์เฟซ) สามารถลงทะเบียนเป็นบริการที่มีชื่อได้ ชื่อที่จดทะเบียนไม่จำเป็นต้องเกี่ยวข้องกับอินเทอร์เฟซหรือชื่อแพ็กเกจ หากไม่ได้ระบุชื่อ ระบบจะใช้ชื่อ "default" ซึ่งควรใช้กับ HAL ที่ไม่จําเป็นต้องลงทะเบียนการใช้งานอินเทอร์เฟซเดียวกัน 2 ครั้ง ตัวอย่างเช่น การเรียก C++ สำหรับการลงทะเบียนบริการที่กําหนดไว้ในแต่ละอินเตอร์เฟซคือ
status_t status = myFoo->registerAsService(); status_t anotherStatus = anotherFoo->registerAsService("another_foo_service"); // if needed
เวอร์ชันของอินเทอร์เฟซ HIDL จะรวมอยู่ในอินเทอร์เฟซนั้นๆ โดยจะเชื่อมโยงกับบริการจดทะเบียนโดยอัตโนมัติและสามารถเรียกดูผ่านการเรียกใช้เมธอด (android::hardware::IInterface::getInterfaceVersion()
) ในอินเทอร์เฟซ HIDL ทุกรายการ ออบเจ็กต์เซิร์ฟเวอร์ไม่จำเป็นต้องลงทะเบียนและสามารถส่งผ่านได้ผ่านพารามิเตอร์เมธอด HIDL ไปยังกระบวนการอื่นที่เรียกใช้เมธอด HIDL ในเซิร์ฟเวอร์
สำรวจบริการ
คำขอตามรหัสไคลเอ็นต์สร้างขึ้นสำหรับอินเทอร์เฟซหนึ่งๆ ตามชื่อและเวอร์ชัน โดยเรียกใช้ getService
ในคลาส HAL ที่ต้องการ
// C++ sp<V1_1::IFooService> service = V1_1::IFooService::getService(); sp<V1_1::IFooService> alternateService = V1_1::IFooService::getService("another_foo_service"); // Java V1_1.IFooService service = V1_1.IFooService.getService(true /* retry */); V1_1.IFooService alternateService = V1_1.IFooService.getService("another", true /* retry */);
ระบบจะถือว่าอินเทอร์เฟซ HIDL แต่ละเวอร์ชันเป็นอินเทอร์เฟซแยกกัน ดังนั้น IFooService
เวอร์ชัน 1.1 และ IFooService
เวอร์ชัน 2.2 จึงสามารถลงทะเบียนเป็น "foo_service" ได้ และ getService("foo_service")
ในอินเทอร์เฟซใดก็ได้จะได้รับบริการที่ลงทะเบียนไว้สำหรับอินเทอร์เฟซนั้น ด้วยเหตุนี้ ในกรณีส่วนใหญ่จึงไม่จำเป็นต้องระบุพารามิเตอร์ชื่อสำหรับการลงทะเบียนหรือการค้นพบ (หมายถึงชื่อ "เริ่มต้น")
ออบเจ็กต์อินเทอร์เฟซของผู้ให้บริการยังมีส่วนเกี่ยวข้องกับวิธีการส่งของอินเทอร์เฟซที่แสดงผลด้วย สำหรับอินเทอร์เฟซ IFoo
ในแพ็กเกจ android.hardware.foo@1.0
อินเทอร์เฟซที่ IFoo::getService
แสดงผลจะใช้วิธีการขนส่งที่ประกาศสำหรับ android.hardware.foo
ในไฟล์ Manifest ของอุปกรณ์เสมอ หากมีรายการดังกล่าว และหากวิธีการขนส่งไม่พร้อมใช้งาน ระบบจะแสดงผล nullptr
ในบางกรณี คุณอาจต้องดำเนินการต่อทันทีแม้ว่าจะยังไม่ได้รับบริการก็ตาม กรณีนี้อาจเกิดขึ้น (เช่น) เมื่อลูกค้าต้องการจัดการการแจ้งเตือนบริการด้วยตนเองหรือในโปรแกรมการวินิจฉัย (เช่น atrace
) ซึ่งจำเป็นต้องรับ hwservices ทั้งหมดและดึงข้อมูล ในกรณีนี้ จะมี API เพิ่มเติม เช่น tryGetService
ใน C++ หรือ getService("instance-name", false)
ใน Java นอกจากนี้ คุณยังต้องใช้ API เดิม getService
ที่ระบุใน Java กับการแจ้งเตือนบริการด้วย การใช้ API นี้ไม่ได้หลีกเลี่ยงเงื่อนไขการแข่งขันที่เซิร์ฟเวอร์จะลงทะเบียนตัวเองหลังจากที่ไคลเอ็นต์ขอด้วย API ที่ไม่ลองใหม่รายการใดรายการหนึ่งเหล่านี้
การแจ้งเตือนการหยุดให้บริการ
ลูกค้าที่ต้องการรับการแจ้งเตือนเมื่อบริการหยุดทำงานจะได้รับการแจ้งเตือนการหยุดทำงานที่เฟรมเวิร์กส่งให้ หากต้องการรับการแจ้งเตือน ลูกค้าต้องมีคุณสมบัติดังนี้
- สร้างคลาส/อินเทอร์เฟซ HIDL ย่อย
hidl_death_recipient
(ในโค้ด C++ ไม่ใช่ใน HIDL) - ลบล้างเมธอด
serviceDied()
- สร้างอินสแตนซ์ของออบเจ็กต์ของคลาสย่อย
hidl_death_recipient
- เรียกใช้เมธอด
linkToDeath()
ในบริการเพื่อตรวจสอบ โดยส่งออบเจ็กต์อินเทอร์เฟซของIDeathRecipient
โปรดทราบว่าเมธอดนี้ไม่ได้เป็นเจ้าของผู้รับในกรณีที่เสียชีวิตหรือพร็อกซีที่เรียกใช้
ตัวอย่างซอร์สโค้ดจำลอง (C++ และ Java คล้ายกัน)
class IMyDeathReceiver : hidl_death_recipient { virtual void serviceDied(uint64_t cookie, wp<IBase>& service) override { log("RIP service %d!", cookie); // Cookie should be 42 } }; .... IMyDeathReceiver deathReceiver = new IMyDeathReceiver(); m_importantService->linkToDeath(deathReceiver, 42);
ผู้รับแจ้งการเสียชีวิตคนเดียวกันสามารถลงทะเบียนในบริการต่างๆ ได้หลายรายการ
การโอนข้อมูล
คุณสามารถส่งข้อมูลไปยังบริการได้โดยเรียกใช้เมธอดที่กําหนดไว้ในอินเทอร์เฟซในไฟล์ .hal
โดยวิธีการมี 2 วิธี ได้แก่
- วิธีการการบล็อกจะรอจนกว่าเซิร์ฟเวอร์จะแสดงผลลัพธ์
- วิธีแบบทางเดียวจะส่งข้อมูลในทิศทางเดียวเท่านั้นและไม่บล็อก หากปริมาณข้อมูลที่ส่งในสายในการเรียก RPC เกินขีดจํากัดการใช้งาน การเรียกอาจบล็อกหรือแสดงข้อบ่งชี้ข้อผิดพลาด (ยังไม่ทราบลักษณะการทํางาน)
เมธอดที่ไม่แสดงผลลัพธ์แต่ไม่ได้ประกาศเป็น oneway
จะยังคงบล็อกอยู่
วิธีการทั้งหมดที่ประกาศในอินเทอร์เฟซ HIDL จะเรียกใช้ในทิศทางเดียวเท่านั้น ไม่ว่าจะจาก HAL ไปยัง HAL หรือจาก HAL ไปยัง HAL อินเทอร์เฟซไม่ได้ระบุทิศทางที่เรียกใช้ สถาปัตยกรรมที่ต้องเรียกใช้จาก HAL ควรมีอินเทอร์เฟซ 2 (หรือมากกว่า) รายการในแพ็กเกจ HAL และแสดงอินเทอร์เฟซที่เหมาะสมจากแต่ละกระบวนการ คําว่าไคลเอ็นต์และเซิร์ฟเวอร์ใช้เพื่อระบุทิศทางการเรียกใช้อินเทอร์เฟซ (กล่าวคือ HAL อาจเป็นเซิร์ฟเวอร์ของอินเทอร์เฟซหนึ่งและเป็นไคลเอ็นต์ของอินเทอร์เฟซอื่น)
การติดต่อกลับ
คําว่า callback หมายถึงแนวคิด 2 อย่างที่แตกต่างกัน ซึ่งแยกความแตกต่างได้ด้วยการเรียกกลับแบบพร้อมกันและการเรียกกลับแบบไม่พร้อมกัน
การเรียกกลับแบบซิงค์ใช้ในเมธอด HIDL บางรายการที่แสดงผลข้อมูล เมธอด HIDL ที่แสดงผลมากกว่า 1 ค่า (หรือแสดงผล 1 ค่าที่เป็นประเภทที่ไม่ใช่แบบพื้นฐาน) จะแสดงผลลัพธ์ผ่านฟังก์ชัน Callback หากระบบแสดงผลค่าเพียงค่าเดียวและเป็นประเภทพื้นฐาน ระบบจะไม่ใช้การเรียกกลับและแสดงผลค่าจากเมธอด เซิร์ฟเวอร์ใช้เมธอด HIDL และไคลเอ็นต์ใช้การเรียกกลับ
การเรียกกลับแบบไม่พร้อมกันช่วยให้เซิร์ฟเวอร์ของอินเทอร์เฟซ HIDL เริ่มต้นการเรียกได้ ซึ่งทำได้โดยการส่งอินสแตนซ์ของอินเทอร์เฟซที่ 2 ผ่านอินเทอร์เฟซแรก ไคลเอ็นต์ของอินเทอร์เฟซแรกต้องทําหน้าที่เป็นเซิร์ฟเวอร์ของอินเทอร์เฟซที่ 2 เซิร์ฟเวอร์ของอินเทอร์เฟซแรกสามารถเรียกเมธอดในออบเจ็กต์อินเทอร์เฟซที่ 2 ได้ ตัวอย่างเช่น การใช้งาน HAL สามารถส่งข้อมูลกลับไปให้กระบวนการที่ใช้ข้อมูลแบบไม่เป็นแบบพร้อมกันได้โดยการเรียกเมธอดในออบเจ็กต์อินเทอร์เฟซที่กระบวนการนั้นสร้างขึ้นและแสดง เมธอดในอินเทอร์เฟซที่ใช้สำหรับ Callback แบบไม่พร้อมกันอาจบล็อก (และอาจแสดงผลลัพธ์ไปยังผู้เรียกใช้) หรือ oneway
โปรดดูตัวอย่างที่ "การเรียกกลับแบบแอซิงโครนัส" ใน HIDL C++
การเรียกใช้เมธอดและการเรียกกลับจะใช้พารามิเตอร์ in
เท่านั้นและไม่รองรับพารามิเตอร์ out
หรือ inout
เพื่อให้การเป็นเจ้าของหน่วยความจำง่ายขึ้น
ขีดจํากัดต่อธุรกรรม
ระบบจะไม่จำกัดจำนวนข้อมูลที่ส่งในเมธอดและฟังก์ชันการเรียกกลับของ HIDL ตามธุรกรรม อย่างไรก็ตาม การเรียกใช้ที่มากกว่า 4 KB ต่อธุรกรรมจะถือว่ามากเกินไป หากเห็นข้อความนี้ เราขอแนะนำให้ปรับโครงสร้างอินเทอร์เฟซ HIDL ดังกล่าวใหม่ ข้อจํากัดอีกประการหนึ่งคือทรัพยากรที่มีให้โครงสร้างพื้นฐาน HIDL เพื่อจัดการธุรกรรมหลายรายการพร้อมกัน ธุรกรรมหลายรายการอาจเกิดขึ้นพร้อมกันได้เนื่องจากมีเธรดหรือกระบวนการหลายรายการที่ส่งการเรียกไปยังกระบวนการหนึ่งๆ หรือการเรียก oneway
หลายรายการที่กระบวนการที่รับไม่จัดการอย่างรวดเร็ว พื้นที่รวมสูงสุดที่พร้อมใช้งานสำหรับธุรกรรมทั้งหมดที่เกิดขึ้นพร้อมกันคือ 1 MB โดยค่าเริ่มต้น
ในอินเทอร์เฟซที่ออกแบบมาอย่างดี ไม่ควรมีการใช้ทรัพยากรเกินขีดจํากัดเหล่านี้ หากเกิดกรณีดังกล่าว การเรียกใช้ที่เกินขีดจํากัดอาจถูกบล็อกจนกว่าจะมีทรัพยากรพร้อมใช้งานหรือส่งสัญญาณข้อผิดพลาดในการขนส่ง ระบบจะบันทึกธุรกรรมที่กำลังดำเนินการอยู่ทั้งหมดที่เกินขีดจำกัดต่อธุรกรรมหรือทรัพยากรการติดตั้งใช้งาน HIDL ล้น เพื่ออำนวยความสะดวกในการแก้ไขข้อบกพร่อง
การใช้งานเมธอด
HIDL จะสร้างไฟล์ส่วนหัวที่ประกาศประเภท เมธอด และ callback ที่จำเป็นในภาษาเป้าหมาย (C++ หรือ Java) โปรโตไทป์ของเมธอดและคำเรียกกลับที่ HIDL กำหนดจะเหมือนกันสำหรับทั้งโค้ดไคลเอ็นต์และเซิร์ฟเวอร์ ระบบ HIDL มีการใช้งานพร็อกซีสำหรับเมธอดฝั่งผู้เรียกใช้ ซึ่งจัดระเบียบข้อมูลสำหรับการขนส่ง IPC และโค้ดสแต็บฝั่งผู้รับสายซึ่งส่งข้อมูลไปยังการใช้งานเมธอดของนักพัฒนาซอฟต์แวร์
ผู้เรียกใช้ฟังก์ชัน (เมธอด HIDL หรือ Callback) เป็นเจ้าของโครงสร้างข้อมูลที่ส่งไปยังฟังก์ชัน และยังคงเป็นเจ้าของหลังจากการเรียกใช้ ไม่ว่าในกรณีใดก็ตาม ผู้ถูกเรียกใช้ไม่จําเป็นต้องเพิ่มหรือปล่อยพื้นที่เก็บข้อมูล
- ใน C++ ข้อมูลอาจเป็นแบบอ่านอย่างเดียว (การพยายามเขียนข้อมูลอาจทำให้เกิดข้อผิดพลาดเกี่ยวกับการจัดสรรหน่วยความจำ) และใช้งานได้ตลอดระยะเวลาการเรียกใช้ ไคลเอ็นต์สามารถคัดลอกข้อมูลแบบเจาะลึกเพื่อนำไปใช้ต่อได้
- ใน Java โค้ดจะได้รับสําเนาข้อมูลในเครื่อง (ออบเจ็กต์ Java ปกติ) ซึ่งอาจเก็บไว้และแก้ไข หรืออนุญาตให้มีการรวบรวมขยะ
การโอนข้อมูลที่ไม่ใช่ RPC
HIDL มีการโอนข้อมูลได้ 2 วิธีโดยไม่ต้องใช้การเรียก RPC ได้แก่ หน่วยความจำที่ใช้ร่วมกันและ Fast Message Queue (FMQ) ซึ่งทั้ง 2 วิธีนี้รองรับเฉพาะใน C++
- หน่วยความจำที่ใช้ร่วมกัน HIDL ประเภท
memory
ในตัวใช้เพื่อส่งออบเจ็กต์ที่แสดงหน่วยความจำที่แชร์ซึ่งจัดสรรไว้แล้ว ใช้ในกระบวนการรับเพื่อจับคู่หน่วยความจําที่แชร์ได้ - คิวข้อความด่วน (FMQ) HIDL มีประเภทคิวข้อความที่ใช้เทมเพลตซึ่งใช้การส่งข้อความแบบไม่รอ และไม่ได้ใช้เคอร์เนลหรือตัวจัดตารางเวลาในโหมดการส่งผ่านหรือโหมด Binderized (การสื่อสารระหว่างอุปกรณ์ไม่มีพร็อพเพอร์ตี้เหล่านี้) โดยปกติแล้ว HAL จะสร้างคิวฝั่งปลายทาง ซึ่งจะสร้างออบเจ็กต์ที่ส่งผ่าน RPC ได้ผ่านพารามิเตอร์ของ HIDL ประเภท
MQDescriptorSync
หรือMQDescriptorUnsync
ในตัว กระบวนการรับสามารถใช้ออบเจ็กต์นี้เพื่อตั้งค่าอีกฝั่งของคิวได้- คิวซิงค์ต้องไม่ล้น และมีเครื่องอ่านได้เพียงเครื่องเดียวเท่านั้น
- คิวไม่ได้ซิงค์จะอนุญาตให้มีรายการเกินจำนวนที่อนุญาตได้ และมีตัวอ่านได้หลายรายการ ซึ่งแต่ละรายการต้องอ่านข้อมูลให้ทันเวลา ไม่เช่นนั้นข้อมูลจะหายไป
ดูรายละเอียดเพิ่มเติมเกี่ยวกับ FMQ ได้ที่คิวข้อความด่วน (FMQ)