บริการ & การถ่ายโอนข้อมูล

ส่วนนี้อธิบายวิธีการลงทะเบียนและค้นหาบริการและวิธีการส่งข้อมูลไปยังบริการโดยการเรียกวิธีการที่กำหนดไว้ในอินเทอร์เฟซในไฟล์ .hal

การลงทะเบียนบริการ

เซิร์ฟเวอร์อินเทอร์เฟซ HIDL (ออบเจ็กต์ที่ใช้อินเทอร์เฟซ) สามารถลงทะเบียนเป็นบริการที่มีชื่อได้ ชื่อที่ลงทะเบียนไม่จำเป็นต้องเกี่ยวข้องกับอินเทอร์เฟซหรือชื่อแพ็คเกจ หากไม่มีการระบุชื่อ จะใช้ชื่อ "ค่าเริ่มต้น" สิ่งนี้ควรใช้สำหรับ HAL ที่ไม่จำเป็นต้องลงทะเบียนการใช้งานสองครั้งของอินเทอร์เฟซเดียวกัน ตัวอย่างเช่น การเรียก 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") บนอินเทอร์เฟซทั้งสองจะได้รับบริการที่ลงทะเบียนสำหรับอินเทอร์เฟซนั้น ด้วยเหตุนี้ ในกรณีส่วนใหญ่ ไม่จำเป็นต้องระบุพารามิเตอร์ชื่อสำหรับการลงทะเบียนหรือการค้นหา (หมายถึงชื่อ "ค่าเริ่มต้น")

Vendor Interface Object ยังมีส่วนร่วมในวิธีการขนส่งของอินเทอร์เฟซที่ส่งคืน สำหรับอินเทอร์เฟซ IFoo ในแพ็คเกจ android.hardware.foo@1.0 อินเทอร์เฟซที่ส่งคืนโดย IFoo::getService จะใช้วิธีการขนส่งที่ประกาศไว้สำหรับ android.hardware.foo ในรายการอุปกรณ์เสมอหากมีรายการอยู่ และหากไม่มีวิธีการขนส่ง ระบบจะส่งคืน nullptr

ในบางกรณีอาจจำเป็นต้องดำเนินการต่อทันทีแม้จะไม่ได้รับบริการก็ตาม สิ่งนี้สามารถเกิดขึ้นได้ (เช่น) เมื่อลูกค้าต้องการจัดการการแจ้งเตือนด้านบริการด้วยตนเองหรือในโปรแกรมวินิจฉัย (เช่น atrace ) ซึ่งจำเป็นต้องรับ hwservices ทั้งหมดและเรียกข้อมูลเหล่านั้น ในกรณีนี้ มีการจัดเตรียม API เพิ่มเติม เช่น tryGetService ใน C++ หรือ getService("instance-name", false) ใน Java getService API เดิมที่มีให้ใน 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 มีสองวิธี:

  • วิธีการ บล็อก รอจนกว่าเซิร์ฟเวอร์จะสร้างผลลัพธ์
  • วิธี เดียว ส่งข้อมูลไปในทิศทางเดียวเท่านั้นและไม่ปิดกั้น หากปริมาณข้อมูลในเที่ยวบินในการเรียก RPC เกินขีดจำกัดการใช้งาน การโทรอาจบล็อกหรือส่งคืนข้อบ่งชี้ข้อผิดพลาด (ยังไม่ได้กำหนดลักษณะการทำงาน)

วิธีการที่ไม่ส่งคืนค่าแต่ไม่ได้ประกาศเป็น oneway ยังคงถูกบล็อกอยู่

วิธีการทั้งหมดที่ประกาศในอินเทอร์เฟซ HIDL จะถูกเรียกในทิศทางเดียว ไม่ว่าจะจาก HAL หรือเข้าสู่ HAL อินเทอร์เฟซไม่ได้ระบุทิศทางที่จะถูกเรียก สถาปัตยกรรมที่ต้องการการเรียกเพื่อเริ่มต้นจาก HAL ควรจัดให้มีอินเทอร์เฟซสองรายการ (หรือมากกว่า) ในแพ็คเกจ HAL และให้บริการอินเทอร์เฟซที่เหมาะสมจากแต่ละกระบวนการ คำว่า ไคลเอนต์ และ เซิร์ฟเวอร์ ถูกใช้โดยคำนึงถึงทิศทางการเรียกของอินเทอร์เฟซ (เช่น HAL สามารถเป็นเซิร์ฟเวอร์ของอินเทอร์เฟซหนึ่งและเป็นไคลเอ็นต์ของอินเทอร์เฟซอื่นได้)

โทรกลับ

คำว่า การโทรกลับ หมายถึงสองแนวคิดที่แตกต่างกัน ซึ่งแยกความแตกต่างจาก การเรียกกลับแบบซิงโครนัส และ การโทรกลับแบบอะซิงโครนัส

การเรียกกลับแบบซิงโครนั สใช้ในวิธี HIDL บางอย่างที่ส่งคืนข้อมูล เมธอด HIDL ที่ส่งคืนค่ามากกว่าหนึ่งค่า (หรือคืนค่าหนึ่งค่าที่ไม่ใช่ชนิดดั้งเดิม) ส่งคืนผลลัพธ์ผ่านฟังก์ชันโทรกลับ หากส่งคืนเพียงค่าเดียวและเป็นประเภทดั้งเดิม จะไม่มีการเรียกกลับและค่าจะถูกส่งกลับจากเมธอด เซิร์ฟเวอร์ใช้วิธีการ HIDL และไคลเอนต์ใช้การเรียกกลับ

การโทรกลับแบบอะซิงโครนัส อนุญาตให้เซิร์ฟเวอร์ของอินเทอร์เฟซ HIDL เริ่มต้นการโทร ซึ่งทำได้โดยการส่งอินสแตนซ์ของอินเทอร์เฟซที่สองผ่านอินเทอร์เฟซแรก ไคลเอนต์ของอินเทอร์เฟซแรกจะต้องทำหน้าที่เป็นเซิร์ฟเวอร์ของอินเทอร์เฟซที่สอง เซิร์ฟเวอร์ของอินเทอร์เฟซแรกสามารถเรียกวิธีการบนวัตถุอินเทอร์เฟซที่สองได้ ตัวอย่างเช่น การใช้งาน HAL อาจส่งข้อมูลแบบอะซิงโครนัสกลับไปยังกระบวนการที่ใช้งานโดยการเรียกวิธีการบนวัตถุอินเทอร์เฟซที่สร้างและให้บริการโดยกระบวนการนั้น วิธีการในอินเทอร์เฟซที่ใช้สำหรับการโทรกลับแบบอะซิงโครนัสอาจถูกบล็อก (และอาจส่งคืนค่าไปยังผู้โทร) หรือ oneway ตัวอย่างเช่น ดู "การเรียกกลับแบบอะซิงโครนัส" ใน HIDL C++

เพื่อให้การเป็นเจ้าของหน่วยความจำง่ายขึ้น การเรียกเมธอดและการเรียกกลับจะใช้เฉพาะ in พารามิเตอร์เท่านั้น และไม่รองรับพารามิเตอร์ out หรือ inout

ขีดจำกัดต่อการทำธุรกรรม

ขีดจำกัดต่อธุรกรรมไม่ได้กำหนดไว้กับจำนวนข้อมูลที่ส่งในวิธี HIDL และการเรียกกลับ อย่างไรก็ตาม การโทรที่เกิน 4KB ต่อธุรกรรมถือว่ามากเกินไป หากพบสิ่งนี้ แนะนำให้ออกแบบอินเทอร์เฟซ HIDL ที่กำหนดใหม่ ข้อจำกัดอีกประการหนึ่งคือทรัพยากรที่มีให้กับโครงสร้างพื้นฐาน HIDL เพื่อจัดการธุรกรรมหลายรายการพร้อมกัน ธุรกรรมหลายรายการสามารถดำเนินการได้พร้อมกันเนื่องจากมีหลายเธรดหรือกระบวนการที่ส่งการเรียกไปยังกระบวนการ หรือการเรียกแบบ oneway หลายครั้งที่ไม่ได้รับการจัดการอย่างรวดเร็วโดยกระบวนการรับ พื้นที่รวมสูงสุดสำหรับธุรกรรมที่เกิดขึ้นพร้อมกันทั้งหมดคือ 1MB ตามค่าเริ่มต้น

ในอินเทอร์เฟซที่ออกแบบมาอย่างดี ไม่ควรเกินขีดจำกัดทรัพยากรเหล่านี้ หากเป็นเช่นนั้น การโทรที่เกินอาจบล็อกจนกว่าทรัพยากรจะพร้อมใช้งานหรือส่งสัญญาณข้อผิดพลาดในการขนส่ง การเกิดขึ้นแต่ละครั้งของการเกินขีดจำกัดต่อธุรกรรมหรือการล้นทรัพยากรการใช้งาน HIDL โดยธุรกรรมบนเครื่องบินแบบรวมจะถูกบันทึกไว้เพื่ออำนวยความสะดวกในการแก้ไขจุดบกพร่อง

การใช้วิธีการ

HIDL สร้างไฟล์ส่วนหัวที่ประกาศประเภท วิธีการ และการเรียกกลับที่จำเป็นในภาษาเป้าหมาย (C++ หรือ Java) ต้นแบบของวิธีการที่กำหนด HIDL และการเรียกกลับจะเหมือนกันสำหรับทั้งรหัสไคลเอนต์และเซิร์ฟเวอร์ ระบบ HIDL จัดเตรียมการใช้งาน พรอกซี ของวิธีการทางฝั่งผู้เรียกซึ่งจัดระเบียบข้อมูลสำหรับการขนส่ง IPC และรหัส stub บนฝั่งผู้ถูกเรียกที่ส่งข้อมูลไปยังการใช้งานของนักพัฒนาวิธีการ

ผู้เรียกฟังก์ชัน (วิธี HIDL หรือการโทรกลับ) เป็นเจ้าของโครงสร้างข้อมูลที่ส่งผ่านไปยังฟังก์ชัน และยังคงรักษาความเป็นเจ้าของหลังจากการเรียก ในทุกกรณีผู้รับสายไม่จำเป็นต้องปล่อยหรือปล่อยที่เก็บข้อมูล

  • ในภาษา C++ ข้อมูลอาจเป็นแบบอ่านอย่างเดียว (การพยายามเขียนอาจทำให้เกิดข้อผิดพลาดในการแบ่งส่วน) และใช้ได้ตลอดระยะเวลาการโทร ไคลเอนต์สามารถคัดลอกข้อมูลเชิงลึกเพื่อเผยแพร่นอกเหนือจากการโทร
  • ใน Java โค้ดจะได้รับสำเนาข้อมูลในเครื่อง (ออบเจ็กต์ Java ปกติ) ซึ่งอาจเก็บและแก้ไขหรืออนุญาตให้มีการรวบรวมขยะ

การถ่ายโอนข้อมูลที่ไม่ใช่ RPC

HIDL มีสองวิธีในการถ่ายโอนข้อมูลโดยไม่ต้องใช้การเรียก RPC: หน่วยความจำที่ใช้ร่วมกันและ Fast Message Queue (FMQ) ซึ่งทั้งสองวิธีรองรับเฉพาะใน C ++

  • หน่วยความจำที่ใช้ร่วมกัน memory ประเภท HIDL ในตัวใช้เพื่อส่งผ่านวัตถุที่แสดงถึงหน่วยความจำที่ใช้ร่วมกันที่ได้รับการจัดสรร สามารถใช้ในกระบวนการรับเพื่อแมปหน่วยความจำที่แชร์
  • คิวข้อความด่วน (FMQ) HIDL จัดให้มีประเภทคิวข้อความเทมเพลตที่ใช้การส่งข้อความโดยไม่ต้องรอ ไม่ใช้เคอร์เนลหรือตัวกำหนดเวลาในโหมดส่งผ่านหรือโหมด Binderized (การสื่อสารระหว่างอุปกรณ์จะไม่มีคุณสมบัติเหล่านี้) โดยทั่วไปแล้ว HAL จะตั้งค่าจุดสิ้นสุดของคิว โดยสร้างอ็อบเจ็กต์ที่สามารถส่งผ่าน RPC ผ่านพารามิเตอร์ของประเภท HIDL ในตัว MQDescriptorSync หรือ MQDescriptorUnsync วัตถุนี้สามารถนำมาใช้โดยกระบวนการรับเพื่อตั้งค่าปลายอีกด้านของคิว
    • คิว การซิงค์ ไม่ได้รับอนุญาตให้ล้น และมีตัวอ่านได้เพียงตัวเดียวเท่านั้น
    • คิว ที่ไม่ซิงค์ ได้รับอนุญาตให้ล้น และอาจมีผู้อ่านจำนวนมาก ซึ่งแต่ละคิวจะต้องอ่านข้อมูลทันเวลา ไม่เช่นนั้นข้อมูลจะสูญหาย
    ทั้งสองประเภทไม่ได้รับอนุญาตให้ underflow (การอ่านจากคิวว่างจะล้มเหลว) และแต่ละประเภทสามารถมีตัวเขียนได้เพียงคนเดียวเท่านั้น

สำหรับรายละเอียดเพิ่มเติมเกี่ยวกับ FMQ โปรดดูที่ Fast Message Queue (FMQ)