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

การเพิ่มห้องสมุด

คุณต้องเพิ่มการพึ่งพาในไลบรารีต้นขั้ว 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 ที่มีอยู่สำหรับแอประบบ วิธีหลังช่วยประหยัดพื้นที่บนอุปกรณ์ สำหรับรายละเอียดเพิ่มเติม โปรดดูที่ Implementing Java SDK Library สำหรับแอปที่เก่ากว่า การทำงานแบบเก่าจะยังคงอยู่

ตั้งแต่ 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 นั้นไม่มีให้ใช้งานบน Boot classpath สำหรับแอพอีกต่อไป (ก่อนหน้านี้ บางครั้งพวกมันถูกใช้เป็น API ที่ซ่อนอยู่ เนื่องจากตัวโหลดคลาสแรกที่ได้รับมอบสิทธิ์ของ Android) พวกเขาถูกย้ายไปยังเนมสเปซใหม่ด้วย jarjar และแอพที่ใช้สิ่งเหล่านี้ (แอพ Priv ที่จำเป็น) จะต้องมีสำเนาแยกต่างหาก โมดูลบนบูตคลาสพาธที่ใช้ 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 จากนั้นเขียนคลาสใหม่เพื่อขยายและใช้วิธีการนามธรรม

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

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

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.
}