HIDL Java

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

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

หน้าต่างๆ ในส่วนนี้จะอธิบายเกี่ยวกับอินเทอร์เฟซ Java สำหรับอินเทอร์เฟซ HIDL, รายละเอียดวิธีสร้าง ลงทะเบียน และใช้บริการ และอธิบายวิธีที่ HAL และไคลเอ็นต์ HAL ที่เขียนด้วย Java โต้ตอบกับระบบ HIDL RPC

ตัวอย่างไคลเอ็นต์

นี่เป็นตัวอย่างไคลเอ็นต์สําหรับอินเทอร์เฟซ IFoo ในแพ็กเกจ android.hardware.foo@1.0 ที่จดทะเบียนเป็นชื่อบริการ default และบริการเพิ่มเติมที่มีชื่อบริการที่กําหนดเอง second_impl

เพิ่มคลัง

คุณต้องเพิ่มการพึ่งพาในไลบรารีสแต็บ HIDL ที่เกี่ยวข้องหากต้องการใช้ โดยปกติแล้วจะเป็นไลบรารีแบบคงที่

// in Android.bp
static_libs: [ "android.hardware.foo-V1.0-java", ],
// in Android.mk
LOCAL_STATIC_JAVA_LIBRARIES += android.hardware.foo-V1.0-java

หากทราบว่าคุณดึงข้อมูล Dependency ในไลบรารีเหล่านี้อยู่แล้ว คุณก็ใช้การลิงก์ที่ใช้ร่วมกันได้เช่นกัน โดยทำดังนี้

// in Android.bp
libs: [ "android.hardware.foo-V1.0-java", ],
// in Android.mk
LOCAL_JAVA_LIBRARIES += android.hardware.foo-V1.0-java

ข้อควรพิจารณาเพิ่มเติมสำหรับการเพิ่มคลังใน Android 10

หากมีแอประบบหรือแอปของผู้ให้บริการที่กำหนดเป้าหมายเป็น Android 10 ขึ้นไป คุณสามารถรวมไลบรารีเหล่านี้แบบคงที่ได้ นอกจากนี้ คุณยังใช้คลาส HIDL (เท่านั้น) จาก JAR ที่กําหนดเองซึ่งติดตั้งในอุปกรณ์ที่มี API ของ Java ที่เสถียรได้โดยใช้กลไก uses-library ที่มีอยู่สําหรับแอประบบ วิธีหลังจะช่วยประหยัดพื้นที่ในอุปกรณ์ ดูรายละเอียดเพิ่มเติมได้ที่การใช้ไลบรารี Java SDK สำหรับแอปเก่า ระบบจะยังคงลักษณะการทำงานแบบเก่าไว้

ตั้งแต่ Android 10 เป็นต้นไป ไลบรารีเหล่านี้จะมีเวอร์ชัน "แบบไม่เจาะลึก" ด้วย ซึ่งรวมถึงชั้นเรียนที่เป็นปัญหา แต่จะไม่รวมชั้นเรียนที่เกี่ยวข้อง ตัวอย่างเช่น android.hardware.foo-V1.0-java-shallow รวมคลาสในแพ็กเกจ foo แต่ไม่ได้รวมคลาสใน android.hidl.base-V1.0-java ซึ่งมีคลาสพื้นฐานของอินเทอร์เฟซ HIDL ทั้งหมด หากคุณสร้างไลบรารีที่มีคลาสพื้นฐานของอินเทอร์เฟซที่ต้องการอยู่แล้วใช้เป็นทรัพยากร คุณจะสามารถใช้สิ่งต่อไปนี้ได้

// in Android.bp
static_libs: [ "android.hardware.foo-V1.0-java-shallow", ],
// in Android.mk
LOCAL_STATIC_JAVA_LIBRARIES += android.hardware.foo-V1.0-java-shallow

นอกจากนี้ ไลบรารีฐานและไลบรารีตัวจัดการ HIDL จะไม่พร้อมใช้งานในบูตคลาสพาธสําหรับแอปอีกต่อไป (ก่อนหน้านี้บางครั้งมีการใช้ไลบรารีเหล่านี้เป็น API ที่ซ่อนอยู่ เนื่องจากคลาสโหลดเดอร์แบบให้สิทธิ์ก่อนของ Android) แต่ระบบได้ย้ายแอตทริบิวต์เหล่านี้ไปยังเนมสเปซใหม่ที่มี jarjar และแอปที่ใช้แอตทริบิวต์เหล่านี้ (แอปที่ต้องเป็นแบบส่วนตัว) ต้องมีสำเนาแยกต่างหาก โมดูลในบูตคลาสพาธที่ใช้ HIDL ต้องใช้ตัวแปรระดับบนสุดของไลบรารี Java เหล่านี้ และเพิ่ม jarjar_rules: ":framework-jarjar-rules" ลงใน Android.bp เพื่อใช้ไลบรารีเหล่านี้ในเวอร์ชันที่มีอยู่ในบูตคลาสพาธ

แก้ไขซอร์สโค้ด Java

บริการนี้มีเพียงเวอร์ชันเดียว (@1.0) ดังนั้นโค้ดนี้จะดึงข้อมูลเฉพาะเวอร์ชันนั้น ดูวิธีจัดการบริการหลายเวอร์ชันได้ที่ส่วนขยายอินเทอร์เฟซ

import android.hardware.foo.V1_0.IFoo;
...
// retry to wait until the service starts up if it is in the manifest
IFoo server = IFoo.getService(true /* retry */); // throws NoSuchElementException if not available
IFoo anotherServer = IFoo.getService("second_impl", true /* retry */);
server.doSomething(…);

ให้บริการ

โค้ดเฟรมเวิร์กใน Java อาจต้องแสดงอินเทอร์เฟซเพื่อรับการเรียกกลับแบบแอซิงโครนัสจาก HAL

สําหรับอินเทอร์เฟซ IFooCallback ในแพ็กเกจ android.hardware.foo เวอร์ชัน 1.0 คุณสามารถใช้อินเทอร์เฟซใน Java ได้โดยทําตามขั้นตอนต่อไปนี้

  1. กำหนดอินเทอร์เฟซใน HIDL
  2. เปิด /tmp/android/hardware/foo/IFooCallback.java เป็นข้อมูลอ้างอิง
  3. สร้างโมดูลใหม่สําหรับการใช้งาน Java
  4. ตรวจสอบคลาส抽象 android.hardware.foo.V1_0.IFooCallback.Stub จากนั้นเขียนคลาสใหม่เพื่อขยายและใช้งานเมธอด抽象

ดูไฟล์ที่สร้างขึ้นโดยอัตโนมัติ

หากต้องการดูไฟล์ที่สร้างขึ้นโดยอัตโนมัติ ให้เรียกใช้คำสั่งต่อไปนี้

hidl-gen -o /tmp -Ljava \
  -randroid.hardware:hardware/interfaces \
  -randroid.hidl:system/libhidl/transport android.hardware.foo@1.0

คำสั่งเหล่านี้จะสร้างไดเรกทอรี /tmp/android/hardware/foo/1.0 สําหรับไฟล์ hardware/interfaces/foo/1.0/IFooCallback.hal การดำเนินการนี้จะสร้างไฟล์ /tmp/android/hardware/foo/1.0/IFooCallback.java ซึ่งจะรวมอินเทอร์เฟซ Java, โค้ดพร็อกซี และสตับ (ทั้งพร็อกซีและสตับเป็นไปตามอินเทอร์เฟซ)

-Lmakefile จะสร้างกฎที่เรียกใช้คำสั่งนี้เมื่อสร้าง และอนุญาตให้คุณรวม android.hardware.foo-V1.0-java และลิงก์กับไฟล์ที่เหมาะสม สคริปต์ที่ทําเช่นนี้โดยอัตโนมัติสําหรับโปรเจ็กต์ที่มีอินเทอร์เฟซมากมายมีอยู่ใน hardware/interfaces/update-makefiles.sh เส้นทางในตัวอย่างนี้เป็นเส้นทางแบบสัมพัทธ์ โดยฮาร์ดแวร์/อินเทอร์เฟซอาจเป็นไดเรกทอรีชั่วคราวในลําดับชั้นโค้ดเพื่อให้คุณพัฒนา HAL ได้ก่อนเผยแพร่

เรียกใช้บริการ

HAL มีอินเทอร์เฟซ IFoo ซึ่งต้องทําการเรียกกลับแบบแอซิงโครนัสไปยังเฟรมเวิร์กผ่านอินเทอร์เฟซ IFooCallback อินเทอร์เฟซ IFooCallback ไม่ได้จดทะเบียนตามชื่อเป็นบริการที่ค้นพบได้ แต่ IFoo ต้องมีเมธอด เช่น setFooCallback(IFooCallback x)

หากต้องการตั้งค่า IFooCallback จากแพ็กเกจ android.hardware.foo เวอร์ชัน 1.0 ให้เพิ่ม android.hardware.foo-V1.0-java ลงใน Android.mk โค้ดเพื่อเรียกใช้บริการคือ

import android.hardware.foo.V1_0.IFoo;
import android.hardware.foo.V1_0.IFooCallback.Stub;
....
class FooCallback extends IFooCallback.Stub {
    // implement methods
}
....
// Get the service from which you will be receiving callbacks.
// This also starts the threadpool for your callback service.
IFoo server = IFoo.getService(true /* retry */); // throws NoSuchElementException if not available
....
// This must be a persistent instance variable, not local,
//   to avoid premature garbage collection.
FooCallback mFooCallback = new FooCallback();
....
// Do this once to create the callback service and tell the "foo-bar" service
server.setFooCallback(mFooCallback);

ส่วนขยายอินเทอร์เฟซ

สมมติว่าบริการหนึ่งๆ ใช้อินเทอร์เฟซ IFoo ในอุปกรณ์ทั้งหมด อุปกรณ์หนึ่งๆ อาจให้บริการที่มีประสิทธิภาพเพิ่มเติมซึ่งติดตั้งใช้งานในส่วนขยายอินเทอร์เฟซ IBetterFoo ดังนี้

interface IFoo {
   ...
};

interface IBetterFoo extends IFoo {
   ...
};

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

IFoo baseService = IFoo.getService(true /* retry */); // throws NoSuchElementException if not available
IBetterFoo extendedService = IBetterFoo.castFrom(baseService);
if (extendedService != null) {
  // The service implements the extended interface.
} else {
  // The service implements only the base interface.
}