HIDL C++

Android O ออกแบบสถาปัตยกรรม Android OS ใหม่เพื่อกำหนดอินเทอร์เฟซที่ชัดเจนระหว่างแพลตฟอร์ม Android ที่ไม่ขึ้นอยู่กับอุปกรณ์และรหัสเฉพาะอุปกรณ์และผู้จำหน่าย Android ได้กำหนดอินเทอร์เฟซดังกล่าวไว้มากมายในรูปแบบของอินเทอร์เฟซ HAL ซึ่งกำหนดเป็นส่วนหัว C ใน hardware/libhardware HIDL แทนที่อินเทอร์เฟซ HAL เหล่านี้ด้วยอินเทอร์เฟซที่มีเวอร์ชันเสถียร ซึ่งสามารถเป็นอินเทอร์เฟซ HIDL ฝั่งไคลเอ็นต์และเซิร์ฟเวอร์ในภาษา C++ (อธิบายไว้ด้านล่าง) หรือ Java

หน้าต่างๆ ในส่วนนี้อธิบายการใช้งาน C++ ของอินเทอร์เฟซ HIDL รวมถึงรายละเอียดเกี่ยวกับไฟล์ที่สร้างขึ้นอัตโนมัติจากไฟล์ HIDL .hal โดยคอมไพลเลอร์ hidl-gen วิธีบรรจุไฟล์เหล่านี้ และวิธีการรวมไฟล์เหล่านี้เข้ากับโค้ด C++ ที่ ใช้พวกเขา

การใช้งานไคลเอนต์และเซิร์ฟเวอร์

อินเทอร์เฟซ HIDL มีการใช้งานไคลเอ็นต์และเซิร์ฟเวอร์:

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

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

รูปที่ 1 ความก้าวหน้าในการพัฒนาสำหรับ HAL แบบเดิม

การสร้างไคลเอ็นต์ HAL

เริ่มต้นด้วยการรวมไลบรารี HAL ไว้ใน makefile:

  • สร้าง: LOCAL_SHARED_LIBRARIES += android.hardware.nfc@1.0
  • เร็ว ๆ นี้: shared_libs: [ …, android.hardware.nfc@1.0 ]

ถัดไป รวมไฟล์ส่วนหัว HAL:

#include <android/hardware/nfc/1.0/IFoo.h>
…
// in code:
sp<IFoo> client = IFoo::getService();
client->doThing();

การสร้างเซิร์ฟเวอร์ HAL

หากต้องการสร้างการใช้งาน HAL คุณต้องมีไฟล์ .hal ที่เป็นตัวแทนของ HAL ของคุณและได้สร้าง makefiles สำหรับ HAL ของคุณแล้วโดยใช้ -Lmakefile หรือ -Landroidbp บน hidl-gen ( ./hardware/interfaces/update-makefiles.sh ทำสิ่งนี้เพื่อ HAL ภายในและเป็นข้อมูลอ้างอิงที่ดี) เมื่อถ่ายโอนผ่าน HAL จาก libhardware คุณสามารถทำงานหลายอย่างได้อย่างง่ายดายโดยใช้ c2hal

หากต้องการสร้างไฟล์ที่จำเป็นเพื่อใช้งาน HAL ของคุณ:

PACKAGE=android.hardware.nfc@1.0
LOC=hardware/interfaces/nfc/1.0/default/
m -j hidl-gen
hidl-gen -o $LOC -Lc++-impl -randroid.hardware:hardware/interfaces \
    -randroid.hidl:system/libhidl/transport $PACKAGE
hidl-gen -o $LOC -Landroidbp-impl -randroid.hardware:hardware/interfaces \
    -randroid.hidl:system/libhidl/transport $PACKAGE

เพื่อให้ HAL ทำงานในโหมดส่งผ่าน คุณต้องมีฟังก์ชัน HIDL_FETCH_IModuleName อยู่ใน /(system|vendor|...)/lib(64)?/hw/android.hardware.package@3.0-impl( OPTIONAL_IDENTIFIER ).so โดยที่ OPTIONAL_IDENTIFIER คือสตริงที่ระบุการใช้งานการส่งผ่าน ข้อกำหนดของโหมดส่งผ่านจะเป็นไปตามคำสั่งด้านบนโดยอัตโนมัติ ซึ่งจะสร้างเป้าหมาย android.hardware.nfc@1.0-impl ด้วยเช่นกัน แต่สามารถใช้ส่วนขยายใดๆ ได้ ตัวอย่างเช่น android.hardware.nfc@1.0-impl-foo ใช้ -foo เพื่อสร้างความแตกต่างให้กับตัวเอง

หาก HAL เป็นเวอร์ชันรองหรือเป็นส่วนขยายของ HAL อื่น HAL ฐานควรใช้เพื่อตั้งชื่อไบนารีนี้ ตัวอย่างเช่น การใช้งาน android.hardware.graphics.mapper@2.1 ควรยังคงอยู่ในไบนารีที่เรียกว่า android.hardware.graphics.mapper@2.0-impl( OPTIONAL_IDENTIFIER ) โดยปกติแล้ว OPTIONAL_IDENTIFIER ที่นี่จะรวมเวอร์ชัน HAL จริงไว้ด้วย ด้วยการตั้งชื่อไบนารีเช่นนี้ ไคลเอนต์ 2.0 สามารถดึงข้อมูลได้โดยตรง และไคลเอนต์ 2.1 สามารถอัปแคสต์การใช้งานได้

จากนั้น กรอกฟังก์ชันการทำงานใน stub และตั้งค่า daemon ตัวอย่างโค้ด daemon (รองรับการส่งผ่าน):

#include <hidl/LegacySupport.h>

int main(int /* argc */, char* /* argv */ []) {
    return defaultPassthroughServiceImplementation<INfc>("nfc");
}

defaultPassthroughServiceImplementation จะ dlopen() ไลบรารี -impl ที่ให้มาและจัดเตรียมเป็นบริการแบบผูกมัด ตัวอย่างโค้ด daemon (สำหรับบริการแบบผูกเดียว):

int main(int /* argc */, char* /* argv */ []) {
    // This function must be called before you join to ensure the proper
    // number of threads are created. The threadpool will never exceed
    // size one because of this call.
    ::android::hardware::configureRpcThreadpool(1 /*threads*/, true /*willJoin*/);

    sp<INfc> nfc = new Nfc();
    const status_t status = nfc->registerAsService();
    if (status != ::android::OK) {
        return 1; // or handle error
    }

    // Adds this thread to the threadpool, resulting in one total
    // thread in the threadpool. We could also do other things, but
    // would have to specify 'false' to willJoin in configureRpcThreadpool.
    ::android::hardware::joinRpcThreadpool();
    return 1; // joinRpcThreadpool should never return
}

โดยปกติแล้ว daemon จะอยู่ใน $PACKAGE + "-service-suffix" (เช่น android.hardware.nfc@1.0-service ) แต่อาจอยู่ที่ใดก็ได้ sepolicy สำหรับคลาสเฉพาะของ HAL คือแอ็ตทริบิวต์ hal_<module> (เช่น hal_nfc) แอ็ตทริบิวต์นี้ต้องใช้กับ daemon ที่รัน HAL เฉพาะ (หากกระบวนการเดียวกันให้บริการ HAL หลายตัว ก็สามารถใช้แอ็ตทริบิวต์หลายตัวกับกระบวนการนั้นได้)