อินเทอร์เฟซ

ทุกอินเทอร์เฟซที่กำหนดไว้ในแพ็คเกจ 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();
    }
    ...
};

เพื่อให้การใช้งานอินเทอร์เฟซเซิร์ฟเวอร์พร้อมใช้งานสำหรับไคลเอ็นต์ คุณสามารถ:

  1. ลงทะเบียน การใช้งานอินเทอร์เฟซกับ hwservicemanager (ดูรายละเอียดด้านล่าง)

    หรือ

  2. ส่งผ่าน การใช้งานอินเทอร์เฟซเป็นอาร์กิวเมนต์ของวิธีการอินเทอร์เฟซ (สำหรับรายละเอียด โปรดดูที่ การเรียกกลับแบบอะซิงโครนัส )

เมื่อลงทะเบียนการใช้งานอินเทอร์เฟซ กระบวนการ 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);