บริการและการโอนข้อมูล

หน้านี้อธิบายวิธีลงทะเบียนและค้นหาบริการ รวมถึงวิธีส่งข้อมูลไปยังบริการโดยการเรียกใช้เมธอดที่กําหนดไว้ในอินเทอร์เฟซในไฟล์ .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 ที่ไม่ลองใหม่รายการใดรายการหนึ่งเหล่านี้

การแจ้งเตือนการหยุดให้บริการ

ลูกค้าที่ต้องการรับการแจ้งเตือนเมื่อบริการหยุดทำงานจะได้รับการแจ้งเตือนการหยุดทำงานที่เฟรมเวิร์กส่งให้ หากต้องการรับการแจ้งเตือน ลูกค้าต้องมีคุณสมบัติดังนี้

  1. สร้างคลาส/อินเทอร์เฟซ HIDL ย่อย hidl_death_recipient (ในโค้ด C++ ไม่ใช่ใน HIDL)
  2. ลบล้างเมธอด serviceDied()
  3. สร้างอินสแตนซ์ของออบเจ็กต์ของคลาสย่อย hidl_death_recipient
  4. เรียกใช้เมธอด 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 ในตัว กระบวนการรับสามารถใช้ออบเจ็กต์นี้เพื่อตั้งค่าอีกฝั่งของคิวได้
    • คิวซิงค์ต้องไม่ล้น และมีเครื่องอ่านได้เพียงเครื่องเดียวเท่านั้น
    • คิวไม่ได้ซิงค์จะอนุญาตให้มีรายการเกินจำนวนที่อนุญาตได้ และมีตัวอ่านได้หลายรายการ ซึ่งแต่ละรายการต้องอ่านข้อมูลให้ทันเวลา ไม่เช่นนั้นข้อมูลจะหายไป
    ไม่อนุญาตให้มีจำนวนที่น้อยกว่า 0 (อ่านจากคิวว่างไม่สำเร็จ) และประเภทแต่ละประเภทจะมีผู้เขียนได้เพียงคนเดียว

ดูรายละเอียดเพิ่มเติมเกี่ยวกับ FMQ ได้ที่คิวข้อความด่วน (FMQ)