שירותים & העברת נתונים

סעיף זה מתאר כיצד לרשום ולגלות שירותים וכיצד לשלוח נתונים לשירות באמצעות שיטות קריאה המוגדרות בממשקים בקבצי .hal .

שירותי רישום

ניתן לרשום שרתי ממשק HIDL (אובייקטים המיישמים את הממשק) כשירותים בעלי שם. השם הרשום אינו חייב להיות קשור לממשק או לשם החבילה. אם לא צוין שם, השם "ברירת מחדל" משמש; זה אמור לשמש עבור HALs שאינם צריכים לרשום שני יישומים של אותו ממשק. לדוגמה, הקריאה C++ לרישום שירות המוגדרת בכל ממשק היא:

status_t status = myFoo->registerAsService();
status_t anotherStatus = anotherFoo->registerAsService("another_foo_service");  // if needed

הגרסה של ממשק HIDL כלולה בממשק עצמו. זה משויך אוטומטית לרישום שירות וניתן לאחזר אותו באמצעות קריאת שיטה ( android::hardware::IInterface::getInterfaceVersion() ) בכל ממשק HIDL. אין צורך לרשום אובייקטי שרת וניתן להעביר אותם באמצעות פרמטרים של שיטת HIDL לתהליך אחר שיבצע קריאות לשיטת HIDL לשרת.

גילוי שירותים

בקשות לפי קוד לקוח מבוצעות עבור ממשק נתון לפי שם ולפי גרסה, הקוראים getService במחלקת HAL הרצויה:

// C++
sp<V1_1::IFooService> service = V1_1::IFooService::getService();
sp<V1_1::IFooService> alternateService = V1_1::IFooService::getService("another_foo_service");
// Java
V1_1.IFooService service = V1_1.IFooService.getService(true /* retry */);
V1_1.IFooService alternateService = V1_1.IFooService.getService("another", true /* retry */);

כל גרסה של ממשק HIDL מטופלת כממשק נפרד. לפיכך, IFooService גרסה 1.1 ו- IFooService גרסה 2.2 יכולות להירשם כ"foo_service" ו- getService("foo_service") בכל אחד מהממשקים מקבל את השירות הרשום עבור אותו ממשק. זו הסיבה שברוב המקרים, אין צורך לספק פרמטר שם לצורך רישום או גילוי (כלומר שם "ברירת מחדל").

אובייקט ממשק הספק ממלא גם חלק בשיטת ההעברה של הממשק המוחזר. עבור ממשק IFoo בחבילה android.hardware.foo@1.0 , הממשק המוחזר על ידי IFoo::getService משתמש תמיד בשיטת התחבורה המוצהרת עבור android.hardware.foo במניפסט המכשיר אם הערך קיים; ואם שיטת ההובלה אינה זמינה, nullptr מוחזר.

במקרים מסוימים, ייתכן שיהיה צורך להמשיך מיד גם מבלי לקבל את השירות. זה יכול לקרות (לדוגמה) כאשר לקוח רוצה לנהל הודעות שירות בעצמו או בתוכנית אבחון (כגון atrace ) שצריכה לקבל את כל שירותי hwservices ולאחזר אותם. במקרה זה, ממשקי API נוספים מסופקים כגון tryGetService ב-C++ או getService("instance-name", false) ב-Java. יש להשתמש ב-API הישן getService המסופק ב-Java גם עם הודעות שירות. השימוש ב-API זה אינו מונע את מצב המירוץ שבו שרת רושם את עצמו לאחר שהלקוח מבקש זאת באמצעות אחד מממשקי ה-API הללו ללא ניסיון חוזר.

הודעות מוות של שירות

לקוחות שרוצים לקבל הודעה כאשר שירות מת, יכולים לקבל הודעות מוות שנמסרו על ידי המסגרת. כדי לקבל הודעות, על הלקוח:

  1. תת-סיווג מחלקה/ממשק HIDL hidl_death_recipient (בקוד C++, לא ב-HIDL).
  2. עוקף את שיטת serviceDied() שלו.
  3. הצג אובייקט של תת-המעמד hidl_death_recipient .
  4. קרא למתודה linkToDeath() בשירות לניטור, תוך העברת אובייקט הממשק של IDeathRecipient . שים לב ששיטה זו אינה לוקחת בעלות על מקבל המוות או על מיופה הכוח אליו היא נקראת.

דוגמה פסאודוקוד (C++ ו-Java דומים):

class IMyDeathReceiver : hidl_death_recipient {
  virtual void serviceDied(uint64_t cookie,
                           wp<IBase>& service) override {
    log("RIP service %d!", cookie);  // Cookie should be 42
  }
};
....
IMyDeathReceiver deathReceiver = new IMyDeathReceiver();
m_importantService->linkToDeath(deathReceiver, 42);

אותו מקבל מוות עשוי להיות רשום במספר שירותים שונים.

העברת נתונים

נתונים עשויים להישלח לשירות באמצעות שיטות קריאה המוגדרות בממשקים בקבצי .hal . יש שני סוגים של שיטות:

  • שיטות החסימה ממתינות עד שהשרת יביא תוצאה.
  • שיטות חד-כיווניות שולחות נתונים לכיוון אחד בלבד ואינן חוסמות. אם כמות הנתונים בטיסה בשיחות RPC חורגת ממגבלות היישום, השיחות עשויות לחסום או להחזיר חיווי שגיאה (ההתנהגות עדיין לא נקבעה).

שיטה שאינה מחזירה ערך אך אינה מוכרזת כ- oneway עדיין חוסמת.

כל השיטות המוצהרות בממשק HIDL נקראות בכיוון יחיד, או מה-HAL או לתוך ה-HAL. הממשק אינו מציין לאיזה כיוון הוא ייקרא. ארכיטקטורות שצריכות קריאות למקור מה-HAL צריכות לספק שני (או יותר) ממשקים בחבילת HAL ולשרת את הממשק המתאים מכל תהליך. המילים לקוח ושרת משמשות ביחס לכיוון הקריאה של הממשק (כלומר, ה-HAL יכול להיות שרת של ממשק אחד ולקוח של ממשק אחר).

התקשרויות חוזרות

המילה התקשרות חוזרת מתייחסת לשני מושגים שונים, המובחנים על ידי התקשרות חוזרת סינכרונית והחזרה אסינכרונית .

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

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

כדי לפשט את הבעלות על הזיכרון, קריאות שיטות והתקשרויות חוזרות in רק פרמטרים ואינן תומכות בפרמטרים out או inout .

מגבלות לכל עסקה

מגבלות לכל עסקה אינן מוטלות על כמות הנתונים הנשלחים בשיטות HIDL והתקשרויות חוזרות. עם זאת, שיחות העולה על 4KB לכל עסקה נחשבות מוגזמות. אם זה נראה, מומלץ לבנות מחדש את ממשק ה-HIDL הנתון. מגבלה נוספת היא המשאבים הזמינים לתשתית HIDL לטיפול במספר עסקאות בו זמנית. עסקאות מרובות יכולות להיות בטיסה בו זמנית עקב שרשורים מרובים או תהליכים שליחת שיחות לתהליך או שיחות oneway מרובות שאינן מטופלות במהירות על ידי תהליך הקבלה. השטח הכולל המרבי הזמין עבור כל העסקאות במקביל הוא 1MB כברירת מחדל.

בממשק מעוצב היטב, חריגה ממגבלות משאבים אלו לא אמורה לקרות; אם כן, השיחה שחרגה מהם עשויה לחסום עד שהמשאבים יהיו זמינים או לאותת על שגיאת העברה. כל אירוע של חריגה ממגבלות לכל עסקה או הצפת משאבי הטמעת HIDL על ידי עסקאות מצטברות בטיסה מתועד כדי להקל על ניפוי הבאגים.

יישומי שיטה

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

למתקשר של פונקציה (שיטת HIDL או התקשרות חוזרת) יש בעלות על מבני הנתונים שהועברו לפונקציה, והוא שומר בבעלות לאחר הקריאה; בכל המקרים, הנמען אינו צריך לפנות או לשחרר את האחסון.

  • ב-C++, הנתונים עשויים להיות לקריאה בלבד (ניסיונות כתיבה אליהם עלולים לגרום לתקלת פילוח) והם תקפים למשך השיחה. הלקוח יכול להעתיק את הנתונים לעומק כדי להפיץ אותם מעבר לשיחה.
  • ב-Java, הקוד מקבל עותק מקומי של הנתונים (אובייקט Java רגיל), שאותו הוא עשוי לשמור ולשנות או לאפשר איסוף אשפה.

העברת נתונים ללא RPC

ל-HIDL יש שתי דרכים להעביר נתונים מבלי להשתמש בשיחת RPC: זיכרון משותף ו-Fast Message Queue (FMQ), שתיהן נתמכות רק ב-C++.

  • זכרון משותף . memory המובנה מסוג HIDL משמש להעברת אובייקט המייצג זיכרון משותף שהוקצה. ניתן להשתמש בתהליך קבלה למיפוי הזיכרון המשותף.
  • תור הודעות מהיר (FMQ) . HIDL מספק סוג תור הודעות בתבנית המיישמת העברת הודעות ללא המתנה. הוא אינו משתמש בליבה או בלוח הזמנים במצב מעבר או מקשר (לתקשורת בין מכשירים לא יהיו מאפיינים אלה). בדרך כלל, ה-HAL מגדיר את קצה התור שלו, ויוצר אובייקט שניתן להעביר דרך RPC באמצעות פרמטר של HIDL מובנה מסוג MQDescriptorSync או MQDescriptorUnsync . ניתן להשתמש באובייקט זה בתהליך הקבלה כדי להגדיר את הקצה השני של התור.
    • תורי סנכרון אינם רשאים לעלות על גדותיהם, ויכולים להיות רק קורא אחד.
    • תורים לא סנכרון רשאים לעלות על גדותיהם, ויכולים להיות להם קוראים רבים, שכל אחד מהם חייב לקרוא נתונים בזמן או לאבד אותם.
    לאף אחד מהסוגים אסור להסתגר (קריאה מתוך תור ריק תיכשל), ולכל סוג יכול להיות רק כותב אחד.

לפרטים נוספים על FMQ, ראה תור הודעות מהיר (FMQ) .