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

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

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

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

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

// 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 และไลบรารีบัญชีดูแลจัดการจะใช้ในการเปิดเครื่องไม่ได้อีก classpath สำหรับแอป (ก่อนหน้านี้บางครั้งถูกใช้เป็น API ที่ซ่อนอยู่เนื่องจาก Classloader แรกที่ได้รับมอบสิทธิ์ของ 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 อาจต้องแสดงผลอินเทอร์เฟซเพื่อรับแบบอะซิงโครนัส Callback จาก 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 ซึ่งต้องทำแบบอะซิงโครนัส Callback ไปยังเฟรมเวิร์กผ่านอินเทอร์เฟซ 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.
}