HIDL Java, HIDL Java

באנדרואיד 8.0 מערכת ההפעלה של אנדרואיד נבנתה מחדש כדי להגדיר ממשקים ברורים בין פלטפורמת אנדרואיד בלתי תלויה במכשיר, לבין קוד ספציפי למכשיר ולספק. אנדרואיד כבר הגדירה ממשקים רבים כאלה בצורה של ממשקי HAL, המוגדרים ככותרות C hardware/libhardware . HIDL החליף את ממשקי HAL אלה בממשקים יציבים עם גרסאות, שיכולים להיות ב-Java (מתואר להלן) או להיות ממשקי HIDL בצד הלקוח והשרת ב- C++ .

ממשקי HIDL נועדו לשמש בעיקר מקוד מקורי, וכתוצאה מכך HIDL מתמקדת ביצירה אוטומטית של קוד יעיל ב-C++. עם זאת, ממשקי HIDL חייבים להיות זמינים לשימוש ישירות מג'אווה, מכיוון שלחלק מתת-מערכות אנדרואיד (כגון טלפוניה) יש ממשקי Java HIDL.

הדפים בסעיף זה מתארים את חזית ה-Java עבור ממשקי HIDL, מפרטים כיצד ליצור, לרשום ולהשתמש בשירותים, ומסבירים כיצד HALs ו-HAL לקוחות שנכתבו ב-Java מקיימים אינטראקציה עם מערכת HIDL RPC.

להיות לקוח

זוהי דוגמה ללקוח עבור ממשק IFoo בחבילה android.hardware.foo@1.0 שרשום כברירת default של שם שירות ושירות נוסף עם שם השירות המותאם אישית second_impl .

הוספת ספריות

אתה צריך להוסיף תלות בספריית ה-HIDL stub המתאימה אם אתה רוצה להשתמש בה. בדרך כלל, זוהי ספרייה סטטית:

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

שיקולים נוספים להוספת ספריות באנדרואיד 10

אם יש לך מערכת או אפליקציית ספק שמכוונת לאנדרואיד 10 ומעלה, אתה יכול לכלול באופן סטטי את הספריות האלה. אתה יכול גם להשתמש (רק) במחלקות HIDL מ-JAR מותאמים אישית המותקנים במכשיר עם ממשקי API יציבים של Java שזמינים באמצעות מנגנון uses-library הקיים עבור אפליקציות מערכת. הגישה האחרונה חוסכת מקום במכשיר. לפרטים נוספים, ראה הטמעת ספריית Java SDK . עבור אפליקציות ישנות יותר, ההתנהגות הישנה נשמרת.

החל מאנדרואיד 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 מוסתר, בשל ה-delegate-first classloader של אנדרואיד). במקום זאת, הם הועברו למרחב שמות חדש עם jarjar , ולאפליקציות שמשתמשות באפליקציות אלו (בהכרח פרטיות) חייבות להיות עותקים נפרדים משלהן. מודולים בנתיב האתחול המשתמשים ב-HIDL חייבים להשתמש בגרסאות הרדודות של ספריות Java אלה וכדי להוסיף jarjar_rules: ":framework-jarjar-rules" ל- Android.bp שלהם כדי להשתמש בגרסה של ספריות אלה שקיימת בנתיב ה-boot class.

שינוי מקור ה-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 יצטרך לשרת ממשקים כדי לקבל התקשרות א-סינכרונית מ-HALs.

עבור ממשק 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 ואת הסטאבים (שניהם פרוקסי ושבבים תואמים את הממשק).

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