อินเทอร์เฟซ

อินเทอร์เฟซทุกรายการที่กําหนดไว้ในแพ็กเกจ 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>

ลูกค้ารับอินเทอร์เฟซได้ 2 วิธีดังนี้

  • ผ่าน I<InterfaceName>::getService (ผ่าน hwservicemanager)
  • ผ่านเมธอดอินเทอร์เฟซ

ไฟล์ส่วนหัวอินเทอร์เฟซที่สร้างขึ้นโดยอัตโนมัติแต่ละไฟล์มีgetServiceวิธีแบบคงที่ซึ่งสามารถใช้ดึงข้อมูลอินสแตนซ์บริการจากhwservicemanagerได้ ดังนี้

// getService returns nullptr if the service can't be found
sp<IFoo> myFoo = IFoo::getService();
sp<IFoo> myAlternateFoo = IFoo::getService("another_foo");

ตอนนี้ไคลเอ็นต์มีอินเทอร์เฟซ IFoo และสามารถเรียกเมธอดเพื่อดำเนินการกับอินเทอร์เฟซนั้นได้ราวกับเป็นการใช้งานคลาสภายใน ในทางปฏิบัติ การติดตั้งใช้งานสามารถทํางานในกระบวนการเดียวกัน กระบวนการอื่น หรือแม้แต่ในอุปกรณ์เครื่องอื่น (ด้วย HAL remoting) เนื่องจากไคลเอ็นต์เรียก getService บนออบเจ็กต์ IFoo ที่รวมอยู่ในแพ็กเกจเวอร์ชัน 1.0 hwservicemanager จึงแสดงผลการใช้งานเซิร์ฟเวอร์เฉพาะในกรณีที่การใช้งานนั้นเข้ากันได้กับไคลเอ็นต์ 1.0 ในทางปฏิบัติ หมายความว่ามีเพียงการใช้งานเซิร์ฟเวอร์ที่มีเวอร์ชัน 1.n (เวอร์ชัน x.(y+1) ของอินเทอร์เฟซต้องขยาย (รับค่ามาจาก) x.y)

นอกจากนี้ ยังมีเมธอด 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);

Callback แบบอะซิงโครนัส

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