HIDL - ג'אווה

ב-Android 8.0, מערכת Android OS יצרה ארכיטקטורה מחדש כדי להגדיר ממשקים ברורים בין פלטפורמת 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

הוספת ספריות

צריך להוסיף יחסי תלות בספריית ה-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 מותאמים אישית שמותקנים במכשיר עם ממשקי API יציבים של Java שזמינים באמצעות מנגנון uses-library הקיים לאפליקציות מערכת. בגישה השנייה חוסכת מקום במכשיר. פרטים נוספים זמינים במאמר הטמעה של ספריית Java SDK. עבור באפליקציות ישנות, ההתנהגות הישנה נשמרת.

החל מ-Android 10, 'shallow' גרסאות של הספריות זמינים גם כן. המסמכים האלה כוללים את הכיתה הרלוונטית, אבל לא כוללים של המחלקות התלויות. לדוגמה, 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 וספריות הניהול כבר לא זמינות באתחול classpath של אפליקציה (לשעבר, הם שימשו לפעמים כ-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 בגרסה 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, את קוד ה-Proxy ואת ה-stubs (גם Proxy וגם stubs תואמים לממשק).

הפקודה -Lmakefile יוצרת את הכללים שמריצים את הפקודה הזו ב-build. ומאפשרת לכם לכלול android.hardware.foo-V1.0-java וקישור אל הקבצים המתאימים. סקריפט שעושה זאת באופן אוטומטי עבור פרויקט מלא הממשקים נמצאים ב-hardware/interfaces/update-makefiles.sh. הנתיבים בדוגמה הזו הם יחסיים; חומרה/ממשקים יכולים להיות שמתחת לעץ הקוד כדי לאפשר לכם לפתח HAL לפני לפרסם אותו.

הפעלת שירות

ממשק ה-HAL מספק את הממשק IFoo, שחייב להיות אסינכרוני קריאות חוזרות ל-framework דרך הממשק של IFooCallback. הממשק של IFooCallback לא רשום לפי שם כממשק שגלוי לכולם service; במקום זאת, IFoo חייב להכיל method כמו 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() method Java כדי להפעיל Cast של ממשק הבסיס באופן בטוח בממשק מורחב:

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