HIDL Java

في الإصدار 8.0 من Android، تمت إعادة تصميم نظام التشغيل لتحديد واجهات واضحة بين منصة Android المستقلة عن الجهاز والرمز المبرمَج الخاص بالجهاز والمورّد. لقد حدّد نظام التشغيل Android العديد من هذه الواجهات في شكل واجهات HAL ، والتي تم تحديدها كرؤوس C في hardware/libhardware. حلّت HIDL محل واجهات HAL هذه بواجهات ثابتة ومُحدَّدة الإصدار، والتي يمكن أن تكون باستخدام Java (كما هو موضّح أدناه) أو واجهات HIDL لجهة العميل والخادم باستخدام C++.

يُقصد استخدام واجهات HIDL بشكل أساسي من الرموز البرمجية الأصلية، ونتيجةً لذلك، تركز HIDL على إنشاء رمز برمجي فعّال تلقائيًا بلغة C++. ومع ذلك، يجب أن تكون واجهات HIDL متاحة أيضًا للاستخدام مباشرةً من Java، لأنّ بعض الأنظمة الفرعية في Android (مثل نظام الهاتف) تتضمّن واجهات Java HIDL.

تصف الصفحات في هذا القسم الواجهة الأمامية لـ Java لواجهات HIDL، ويشرح بالتفصيل كيفية إنشاء الخدمات وتسجيلها واستخدامها، ويوضّح كيفية تفاعل واجهات HAL وHAL العملاء المكتوبة بلغة Java مع نظام HIDL RPC.

مثال على العميل

في ما يلي مثال على واجهة IFoo في الحزمة android.hardware.foo@1.0 المسجَّلة باسم الخدمة default وخدمة إضافية باسم الخدمة المخصّص second_impl.

إضافة مكتبات

عليك إضافة تبعيات على مكتبة HIDL stub library المقابلة إذا أردت استخدامها. وعادةً ما تكون هذه مكتبة ثابتة:

// 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

إذا كان لديك تطبيق نظام أو مطوِّر يستهدف الإصدار 10 من نظام التشغيل Android أو إصدارًا أحدث، يمكنك تضمين هذه المكتبات بشكل ثابت. يمكنك أيضًا استخدام فئات HIDL (فقط) من حِزم JAR المخصّصة المثبَّتة على الجهاز مع توفير واجهات برمجة تطبيقات 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 الأساسية والمديرة متوفّرتان أيضًا في ملف مسار تحميل تطبيقات التمهيد (كانتا تُستخدَمان في السابق كواجهة برمجة تطبيقات مخفية، وذلك بسبب ملف تحميل الفئات الذي يعتمد على المفوّض أولاً في Android). بدلاً من ذلك، تم نقلها إلى مساحة اسم جديدة باستخدام jarjar، ويجب أن تتضمّن التطبيقات التي تستخدم هذه التطبيقات (priv apps) نسخًا منفصلة خاصة بها. يجب أن تستخدم الوحدات في مسار تحميل التمهيد التي تستخدم 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 من الإصدار 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.
}