HIDL Java

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

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

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

เป็นลูกค้า

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

การเพิ่มไลบรารี

คุณต้องเพิ่มการขึ้นต่อกันในไลบรารี stub 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

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

// 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 แบบกำหนดเองที่ติดตั้งบนอุปกรณ์ด้วย Java API ที่เสถียรซึ่งพร้อมใช้งานโดยใช้กลไก 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 ที่ซ่อนอยู่ เนื่องจาก Classloader แรกของผู้รับมอบสิทธิ์ของ 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 ในเวอร์ชัน 1.0 ของแพ็คเกจ android.hardware.foo คุณสามารถใช้อินเทอร์เฟซของคุณใน Java ได้โดยใช้ขั้นตอนต่อไปนี้:

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

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

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

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 รหัสพร็อกซี และ stubs (ทั้งพร็อกซี และต้นขั้วสอดคล้องกับอินเทอร์เฟซ)

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

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

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

หากต้องการตั้ง IFooCallback จากเวอร์ชัน 1.0 ของแพ็คเกจ android.hardware.foo ให้เพิ่ม 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.
}