Sensors HAL 2.0

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

‫Sensors HAL 2.0 זמין ב-Android מגרסה 10 ואילך במכשירים חדשים ובמכשירים ששודרגו. ‫Sensors HAL 2.0 מבוסס על Sensors HAL 1.0, אבל יש כמה הבדלים חשובים שמונעים תאימות לאחור. ‫Sensors HAL 2.0 משתמש בתורים מהירים של הודעות (FMQ) כדי לשלוח אירועי חיישן מ-HAL למסגרת החיישן של Android.

‫Sensors HAL 2.1 זמין ב-Android מגרסה 11 ואילך במכשירים חדשים ובמכשירים משודרגים. ‫Sensors HAL 2.1 הוא איטרציה של Sensors HAL 2.0 שחושפת את סוג החיישן HINGE_ANGLE ומעדכנת שיטות שונות כדי לקבל את הסוג HINGE_ANGLE.

ממשק HAL 2.1

המקור העיקרי לתיעוד של Sensors HAL 2.1 נמצא בהגדרת ה-HAL בכתובת hardware/interfaces/sensors/2.1/ISensors.hal. אם יש סתירה בין הדרישות שמופיעות בדף הזה לבין הדרישות שמופיעות בISensors.hal, הדרישות שמופיעות בISensors.hal הן הקובעות.

ממשק HAL 2.0

המקור העיקרי לתיעוד של Sensors HAL 2.0 נמצא בהגדרת ה-HAL בכתובת hardware/interfaces/sensors/2.0/ISensors.hal. אם יש סתירה בין הדרישות שמופיעות בדף הזה לבין הדרישות שמופיעות בISensors.hal, הדרישות שמופיעות בISensors.hal הן הקובעות.

הטמעה של Sensors HAL 2.0 ו-HAL 2.1

כדי להטמיע את Sensors HAL 2.0 או 2.1, אובייקט צריך להרחיב את הממשק ISensors ולהטמיע את כל הפונקציות שמוגדרות ב-2.0/ISensors.hal או ב-2.1/ISensors.hal.

אתחול ה-HAL

לפני שניתן להשתמש ב-HAL של החיישנים, צריך לאתחל אותו באמצעות מסגרת החיישנים של Android. המסגרת קוראת לפונקציה initialize() עבור HAL 2.0 ולפונקציה initialize_2_1() עבור HAL 2.1 כדי לספק שלושה פרמטרים ל-Sensors HAL: שני מתארים של FMQ ומצביע אחד לאובייקט ISensorsCallback.

שכבת ה-HAL משתמשת בתיאור הראשון כדי ליצור את FMQ של האירוע שמשמש לכתיבת אירועי חיישן למסגרת. ה-HAL משתמש בתיאור השני כדי ליצור את ה-FMQ של WakeLock שמשמש לסנכרון כשה-HAL משחרר את ה-WakeLock שלו לאירועי חיישן WAKE_UP. שכבת ה-HAL צריכה לשמור מצביע לאובייקט ISensorsCallback כדי שאפשר יהיה להפעיל את כל פונקציות ההתקשרות חזרה הנדרשות.

הפונקציה initialize() או הפונקציה initialize_2_1() חייבות להיות הפונקציה הראשונה שמופעלת כשמאתחלים את Sensors HAL.

חשיפה של חיישנים זמינים

כדי לקבל רשימה של כל החיישנים הסטטיים הזמינים במכשיר, משתמשים בפונקציה getSensorsList() ב-HAL 2.0 ובפונקציה getSensorsList_2_1() ב-HAL 2.1. הפונקציה מחזירה רשימה של חיישנים, שכל אחד מהם מזוהה באופן ייחודי על ידי נקודת האחיזה שלו. הכינוי של חיישן מסוים לא יכול להשתנות כשהתהליך שמארח את Sensors HAL מופעל מחדש. יכול להיות שהכינויים ישתנו אחרי הפעלה מחדש של המכשיר, וגם אחרי הפעלה מחדש של שרת המערכת.

אם כמה חיישנים חולקים את אותו סוג חיישן ואת אותה תכונת התעוררות, החיישן הראשון ברשימה נקרא חיישן ברירת המחדל והוא מוחזר לאפליקציות שמשתמשות בפונקציה getDefaultSensor(int sensorType, bool wakeUp).

יציבות של רשימת החיישנים

אחרי הפעלה מחדש של Sensors HAL, אם הנתונים שמוחזרים על ידי getSensorsList() או getSensorsList_2_1() מצביעים על שינוי משמעותי בהשוואה לרשימת החיישנים שאוחזרה לפני ההפעלה מחדש, המסגרת מפעילה מחדש את זמן הריצה של Android. שינויים משמעותיים ברשימת החיישנים כוללים מקרים שבהם חסר חיישן עם נקודת אחיזה נתונה או שהמאפיינים שלו השתנו, או מקרים שבהם נוספו חיישנים חדשים. למרות שהפעלה מחדש של זמן הריצה של Android משבשת את חוויית המשתמש, היא נדרשת כי מסגרת Android לא יכולה יותר לעמוד בחוזה של Android API, שלפיו חיישנים סטטיים (לא דינמיים) לא משתנים במהלך משך החיים של אפליקציה. יכול להיות שהפעולה הזו גם תמנע מהמסגרת להקים מחדש בקשות פעילות של חיישנים שמוגשות על ידי אפליקציות. לכן, מומלץ לספקי HAL למנוע שינויים ברשימת החיישנים שאפשר להימנע מהם.

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

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

הגדרת חיישנים

לפני שמפעילים חיישן, צריך להגדיר אותו באמצעות הפונקציה batch() עם תקופת דגימה וחביון דיווח מקסימלי.

חייבים להיות מסוגלים להגדיר מחדש את החיישן בכל שלב באמצעות batch() בלי לאבד את נתוני החיישן.

תקופת הדגימה

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

  • רציף: אירועי חיישן נוצרים בקצב רציף.
  • בשינוי: האירועים נוצרים בקצב שלא עולה על קצב תקופת הדגימה, ויכול להיות שהם ייווצרו בקצב איטי יותר אם הערך שנמדד לא משתנה.
  • חד-פעמי: המערכת מתעלמת מתקופת הדגימה.
  • מיוחד: פרטים נוספים זמינים במאמר סוגי חיישנים.

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

זמן המתנה מקסימלי לדיווח

החביון המקסימלי של הדיווח מגדיר את הזמן המקסימלי בננו-שניות שבו אפשר לעכב אירועים ולאחסן אותם ב-FIFO של החומרה לפני שהם נכתבים ל-FMQ של האירועים דרך ה-HAL בזמן שה-SoC פעיל.

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

לדוגמה, מד תאוצה שמופעל ב-50 Hz עם זמן אחזור מקסימלי של אפס, מפעיל הפרעות 50 פעמים בשנייה כשה-SoC פעיל.

אם זמן האחזור המקסימלי של הדיווח גדול מאפס, אין צורך לדווח על אירועי חיישן ברגע שהם מזוהים. אפשר לאחסן אירועים באופן זמני ב-FIFO של החומרה ולדווח עליהם בקבוצות, כל עוד אף אירוע לא מתעכב יותר מזמן האחזור המקסימלי של הדיווח. כל האירועים מאז הקבוצה הקודמת מתועדים ומוחזרים בבת אחת. כך מצמצמים את מספר ההפסקות שנשלחות ל-SoC ומאפשרים ל-SoC לעבור למצב צריכת חשמל נמוכה יותר בזמן שהחיישן אוסף נתונים ומקבץ אותם.

לכל אירוע משויכת חותמת זמן. השהיית הזמן שבו מדווח על אירוע לא יכולה להשפיע על חותמת הזמן של האירוע. חותמת הזמן צריכה להיות מדויקת ולשקף את השעה שבה האירוע התרחש בפועל, ולא את השעה שבה הוא דווח.

מידע נוסף על דיווח אירועים של חיישנים עם זמן חביון מקסימלי לדיווח שאינו אפס ודרישות נוספות זמין במאמר בנושא אצווה.

הפעלת חיישנים

המסגרת מפעילה ומשביתה חיישנים באמצעות הפונקציה activate(). לפני שמפעילים חיישן, המסגרת צריכה להגדיר את החיישן באמצעות batch().

אחרי שחיישן מושבת, אסור לכתוב אירועים נוספים מהחיישן הזה ל-FMQ של האירועים.

חיישני הדחה

אם חיישן מוגדר לאגור נתוני חיישן, אפשר להשתמש ב-flush() כדי לאלץ את המסגרת לנקות את אירועי החיישן המאוגדים באופן מיידי. הפעולה הזו גורמת לכך שאירועי החיישן באצווה עבור ה-handle של החיישן שצוין ייכתבו באופן מיידי ל-Event FMQ. מודול ה-HAL של החיישנים חייב לצרף אירוע של השלמת ניקוי בסוף אירועי החיישנים שנכתבים כתוצאה מקריאה ל-flush().

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

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

אם קוראים ל-flush() עבור חיישן חד-פעמי, הפונקציה flush() חייבת להחזיר BAD_VALUE ולא ליצור אירוע של סיום הניקוי.

כתיבת אירועי חיישן ל-FMQ

התור FMQ של האירועים משמש את Sensors HAL כדי לדחוף אירועים של חיישנים למסגרת החיישנים של Android.

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

אחרי ש-Sensors HAL כותב את מספר אירועי החיישן הרצוי ל-Event FMQ, הוא צריך להודיע למסגרת שהאירועים מוכנים. כדי לעשות זאת, הוא כותב את הביט EventQueueFlagBits::READ_AND_PROCESS לפונקציה EventFlag::wake של Event FMQ. אפשר ליצור את EventFlag מ-Event FMQ באמצעות EventFlag::createEventFlag והפונקציה getEventFlagWord() של Event FMQ.

‫HAL 2.0/2.1 של חיישנים תומך ב-write וב-writeBlocking ב-Event FMQ. ההטמעה שמוגדרת כברירת מחדל מספקת הפניה לשימוש ב-write. אם משתמשים בפונקציה writeBlocking, צריך להגדיר את הדגל readNotification לערך EventQueueFlagBits::EVENTS_READ, שמוגדר על ידי המסגרת כשהיא קוראת אירועים מ-Event FMQ. דגל ההתראה על כתיבה צריך להיות מוגדר לערך EventQueueFlagBits::READ_AND_PROCESS, שמציין למסגרת שאירועים נכתבו ל-FMQ של האירועים.

אירועי WAKE_UP

אירועים מסוג WAKE_UP הם אירועים של חיישנים שגורמים למעבד האפליקציות (AP) להתעורר ולטפל באירוע באופן מיידי. בכל פעם שאירוע WAKE_UP נכתב ל-Event FMQ, ‏ Sensors HAL חייב להבטיח שהמערכת תישאר פעילה עד שהמסגרת תוכל לטפל באירוע. כשמתקבל אירוע WAKE_UP, המסגרת מאבטחת את חסימת מצב השינה שלה, וכך מאפשרת ל-Sensors HAL לשחרר את חסימת מצב השינה שלו. כדי לסנכרן כשה-HAL של החיישנים משחרר את נעילת ההשכמה שלו, משתמשים ב-FMQ של נעילת ההשכמה.

ה-HAL של חיישנים צריך לקרוא את ה-FMQ של Wake Lock כדי לקבוע את מספר WAKE_UP האירועים שהמסגרת טיפלה בהם. ה-HAL צריך לשחרר את נעילת ההשכמה שלו לאירועי WAKE_UP רק אם המספר הכולל של אירועי WAKE_UP שלא טופלו הוא אפס. אחרי הטיפול באירועים של חיישנים, המסגרת סופרת את מספר האירועים שסומנו כאירועי WAKE_UP וכותבת את המספר הזה בחזרה ל-FMQ של Wake Lock.

המסגרת מגדירה את WakeLockQueueFlagBits::DATA_WRITTEN write notification ב-Wake Lock FMQ בכל פעם שהיא כותבת נתונים ב-Wake Lock FMQ.

חיישנים דינמיים

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

כשחיישן דינמי מחובר, צריך לקרוא לפונקציה onDynamicSensorConnected ב-ISensorsCallback מ-Sensors HAL. ההודעה הזו מעדכנת את המסגרת לגבי החיישן הדינמי החדש, ומאפשרת לשלוט בחיישן דרך המסגרת ולצרוך את האירועים של החיישן על ידי לקוחות.

באופן דומה, כשחיישן דינמי מנותק, צריך להפעיל את הפונקציה onDynamicSensorDisconnected ב-ISensorsCallback כדי שהמסגרת תוכל להסיר חיישן שכבר לא זמין.

ערוץ ישיר

ערוץ ישיר הוא שיטת פעולה שבה אירועים של חיישנים נכתבים בזיכרון ספציפי במקום ב-Event FMQ, תוך עקיפה של Android Sensors Framework. לקוח שרושם ערוץ ישיר חייב לקרוא את אירועי החיישן ישירות מהזיכרון ששימש ליצירת הערוץ הישיר, ולא יקבל את אירועי החיישן דרך המסגרת. הפונקציה configDirectReport() דומה לפונקציה batch() בפעולה רגילה, והיא מגדירה את ערוץ הדיווח הישיר.

הפונקציות registerDirectChannel() ו-unregisterDirectChannel() יוצרות או משמידות ערוץ ישיר חדש.

מצבי הפעולה

הפונקציה setOperationMode() מאפשרת למסגרת להגדיר חיישן כדי שהמסגרת תוכל להזריק נתוני חיישנים לחיישן. האפשרות הזו שימושית לבדיקות, במיוחד לאלגוריתמים שקיימים מתחת למסגרת.

הפונקציה injectSensorData() ב-HAL 2.0 והפונקציה injectSensorsData_2_1() ב-HAL 2.0 משמשות בדרך כלל להעברת פרמטרים תפעוליים אל Sensors HAL. אפשר להשתמש בפונקציה גם כדי להחדיר אירועים של חיישנים לחיישן ספציפי.

אימות

כדי לאמת את ההטמעה של Sensors HAL, מריצים את בדיקות ה-CTS וה-VTS של החיישן.

בדיקות CTS

בדיקות Sensor CTS קיימות גם בבדיקות CTS אוטומטיות וגם באפליקציית CTS Verifier ידנית.

הבדיקות האוטומטיות ממוקמות ב-cts/tests/sensor/src/android/hardware/cts. הבדיקות האלה מאמתות את הפונקציונליות הרגילה של החיישנים, כמו הפעלה של חיישנים, עיבוד באצווה ושיעורי אירועים של חיישנים.

הבדיקות של CTS Verifier נמצאות בתיקייה cts/apps/CtsVerifier/src/com/android/cts/verifier/sensors. הבדיקות האלה דורשות הזנה ידנית ממפעיל הבדיקה, והן מבטיחות שהחיישנים ידווחו על ערכים מדויקים.

הצלחה בבדיקות CTS היא חיונית כדי לוודא שהמכשיר שנבדק עומד בכל הדרישות של CDD.

בדיקות VTS

בדיקות VTS עבור Sensors HAL 2.0 ממוקמות בתיקייה hardware/interfaces/sensors/2.0/vts. בדיקות VTS עבור Sensors HAL 2.1 נמצאות בכתובת hardware/interfaces/sensors/2.1/vts. הבדיקות האלה מוודאות שה-HAL של החיישנים מיושם בצורה נכונה ושהדרישות ב-ISensors.hal וב-ISensorsCallback.hal מתקיימות בצורה תקינה.

שדרוג ל-Sensors HAL 2.1 מגרסה 2.0

כשמשדרגים ל-Sensors HAL 2.1 מגרסה 2.0, הטמעת ה-HAL חייבת לכלול את השיטות initialize_2_1(), getSensorsList_2_1() ו-injectSensorsData_2_1(), יחד עם הסוגים של HAL 2.1. השיטות האלה צריכות לעמוד באותן דרישות שמפורטות למעלה לגבי HAL 2.0.

מכיוון ש-HALs של גרסאות משניות חייבים לתמוך בכל הפונקציות מ-HALs קודמים, ‏ HALs בגרסה 2.1 חייבים לתמוך באתחול כ-HALs בגרסה 2.0. כדי להימנע מהמורכבות של תמיכה בשתי גרסאות HAL, מומלץ מאוד להשתמש ב-Multi-HAL 2.1.

דוגמה להטמעה של Sensors 2.1 HAL משלכם מופיעה ב-Sensors.h.

שדרוג מ-Sensors HAL 1.0 ל-2.0

כשמשדרגים מ-HAL 1.0 ל-HAL 2.0 של חיישנים, צריך לוודא שההטמעה של HAL עומדת בדרישות הבאות.

אתחול ה-HAL

הפונקציה initialize() צריכה להיות נתמכת כדי ליצור תורי הודעות מהירים בין המסגרת לבין HAL.

חשיפה של חיישנים זמינים

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

כתיבת אירועי חיישן ל-FMQ

במקום לחכות לקריאה של poll(), ב-Sensors HAL 2.0, ‏ Sensors HAL צריך לכתוב באופן יזום אירועים של חיישנים ל-Event FMQ בכל פעם שאירועים של חיישנים זמינים. ה-HAL אחראי גם לכתיבת הביטים הנכונים ל-EventFlag כדי לגרום לקריאת FMQ במסגרת.

אירועי WAKE_UP

ב-Sensors HAL 1.0, ה-HAL יכול היה לבטל את נעילת ההשכמה שלו לכל WAKE_UP אירוע בכל קריאה עוקבת ל-poll() אחרי ש-WAKE_UP פורסם ב-poll(), כי זה הצביע על כך שהמסגרת עיבדה את כל אירועי החיישן וקיבלה נעילת השכמה, אם היה צורך בכך. ב-Sensors HAL 2.0,‏ HAL כבר לא יודע מתי המסגרת עיבדה אירועים שנכתבו ל-FMQ, ולכן ה-FMQ של Wake Lock מאפשר למסגרת לתקשר עם HAL כשהיא מטפלת באירועים של WAKE_UP.

ב-Sensors HAL 2.0, נעילת ההשכמה שמאובטחת על ידי Sensors HAL עבור WAKE_UP אירועים חייבת להתחיל ב-SensorsHAL_WAKEUP.

חיישנים דינמיים

חיישנים דינמיים הוחזרו באמצעות הפונקציה poll() ב-Sensors HAL 1.0. ב-Sensors HAL 2.0 נדרש לקרוא ל-onDynamicSensorsConnected ול-onDynamicSensorsDisconnected ב-ISensorsCallback בכל פעם שמשתנים חיבורים של חיישנים דינמיים. הקריאות החוזרות האלה זמינות כחלק מהמצביע ISensorsCallback שמסופק דרך הפונקציה initialize().

מצבי הפעולה

חייבת להיות תמיכה במצב DATA_INJECTION של חיישני WAKE_UP ב-Sensors HAL 2.0.

תמיכה ב-Multi-HAL

‫Sensors HAL 2.0 ו-2.1 תומכים ב-multi-HAL באמצעות ה-framework של Sensors Multi-HAL. פרטים על ההטמעה מופיעים במאמר העברה מ-Sensors HAL 1.0.