อินเทอร์เฟซ

ทุกอินเทอร์เฟซที่กำหนดไว้ในแพ็กเกจ 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. ส่งการติดตั้งใช้งานอินเทอร์เฟซเป็นอาร์กิวเมนต์ของ เมธอดของอินเทอร์เฟซ (สำหรับการแยกส่วน โปรดดูอะซิงโครนัส Callback)

เมื่อลงทะเบียนการใช้งานอินเทอร์เฟซ กระบวนการ 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) เนื่องจากลูกค้าโทรหา 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> ออบเจ็กต์ ช่วยจัดเก็บ Callback และ กลับเข้าไปในไคลเอ็นต์ เมื่อใดก็ตามที่ต้องการใช้อินเทอร์เฟซนี้

ผู้รับการเสียชีวิต

เนื่องจากการใช้บริการอาจทำงานในกระบวนการอื่น จึงอาจเกิดขึ้นได้ กระบวนการที่ใช้อินเทอร์เฟซจะหายไปขณะที่ไคลเอ็นต์ยังทำงานอยู่ การเรียกใช้ออบเจ็กต์อินเทอร์เฟซที่โฮสต์ในกระบวนการที่หยุดทำงานแล้วไม่สำเร็จ มีข้อผิดพลาดการขนส่ง (isOK() แสดงผล false) วิธีเดียวในการ กู้คืนจากความล้มเหลวดังกล่าว คือการขอบริการอินสแตนซ์ใหม่โดย กำลังโทรหา I<InterfaceName>::getService() วิธีนี้ใช้ได้ก็ต่อเมื่อ กระบวนการที่ขัดข้องได้เริ่มต้นใหม่และลงทะเบียนบริการอีกครั้งด้วย servicemanager (ซึ่งโดยทั่วไปจะเป็นจริงสำหรับการติดตั้งใช้งาน HAL)

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

foo->linkToDeath(recipient, 1481 /* cookie */);

พารามิเตอร์ recipient ต้องเป็นการใช้งาน android::hardware::hidl_death_recipient มาจาก HIDL ซึ่งมีเมธอด serviceDied() เดียวที่เรียกว่า จากเทรดใน Threadpool ของ 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);