שכבת ההפשטה של חומרת החיישנים (HAL) היא הממשק בין מסגרת החיישנים של Android לבין חיישני המכשיר, כמו מד תאוצה או ג'ירוסקופ. תקן HAL לחיישנים מגדיר את הפונקציות שצריך להטמיע כדי לאפשר ל-framework לשלוט בחיישנים.
גרסה HAL 2.0 של החיישנים זמינה ב-Android מגרסה 10 ואילך למכשירים חדשים ומשודרגים. Sensors HAL 2.0 מבוסס על Sensors HAL 1.0, אבל יש כמה הבדלים עיקריים שמונעים ממנו להיות תואם לאחור. ב-Sensors HAL 2.0 נעשה שימוש בQueues Message Fast (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 כדי לספק שלושה פרמטרים ל-HAL של החיישנים: שני מתארי FMQ ו-pointer אחד לאובייקט ISensorsCallback
.
ה-HAL משתמש בתיאור הראשון כדי ליצור את ה-Event FMQ שמשמש לכתיבת אירועי חיישנים למסגרת. ה-HAL משתמש במתאר השני כדי ליצור את ה-FMQ של Wake Lock, שמשמשים לסנכרון כשה-HAL משחרר את נעילת ההתעוררות שלו לאירועי חיישן WAKE_UP
. ה-HAL חייב לשמור מצביע לאובייקט ISensorsCallback
כדי שאפשר יהיה להפעיל את כל פונקציות ההתקשרות החוזרת הנדרשות.
הפונקציה initialize()
או initialize_2_1()
חייבת להיות הפונקציה הראשונה שנקראת במהלך האיפוס של Sensors HAL.
חשיפת החיישנים הזמינים
כדי לקבל רשימה של כל החיישנים הסטטיים הזמינים במכשיר, משתמשים בפונקציה getSensorsList()
ב-HAL 2.0 ובפונקציה getSensorsList_2_1()
ב-HAL 2.1. הפונקציה הזו מחזירה רשימה של חיישנים, שכל אחד מהם מזוהה באופן ייחודי לפי הכינוי שלו. ה-handle של חיישן נתון לא יכול להשתנות כשהתהליך שמארח את Sensors HAL מופעל מחדש. הכינויים עשויים להשתנות לאחר הפעלה מחדש של המכשיר, וגם לאחר הפעלה מחדש של שרת המערכת.
אם לכמה חיישנים יש אותו סוג חיישן ואותו מאפיין יציאה ממצב שינה, החיישן הראשון ברשימה נקרא חיישן ברירת המחדל והוא מוחזר לאפליקציות שמשתמשות בפונקציה getDefaultSensor(int sensorType, bool wakeUp)
.
יציבות רשימת החיישנים
אחרי הפעלה מחדש של ממשק החיישן עם HAL, אם הנתונים שהוחזרו על ידי getSensorsList()
או getSensorsList_2_1()
מעידים על שינוי משמעותי בהשוואה לרשימת החיישנים שאוחזרה לפני ההפעלה מחדש, ה-framework יפעיל מחדש את סביבת זמן הריצה של Android. שינויים משמעותיים ברשימת החיישנים כוללים מקרים שבהם חסר חיישן עם ידית מסוימת או שהמאפיינים שלו השתנו, או שבהם נוספו חיישנים חדשים. הפעלה מחדש של סביבת זמן הריצה של Android מפריעה למשתמש, אבל היא נדרשת כי מסגרת Android כבר לא יכולה לעמוד בתנאי החוזה של Android API, שלפיו חיישני סטטיים (לא דינמיים) לא משתנים במהלך מחזור החיים של אפליקציה. כתוצאה מכך, יכול להיות שהמסגרת לא תוכל ליצור מחדש בקשות חיישנים פעילות שנשלחות על ידי אפליקציות. לכן, מומלץ לספקים של HAL למנוע שינויים ברשימת החיישנים שאפשר להימנע מהם.
כדי להבטיח שמזהי החיישנים יהיו יציבים, ה-HAL צריך למפות באופן דטרמיניסטי חיישן פיזי נתון במכשיר למזהה שלו. ממשק ה-HAL של חיישנים לא מחייב הטמעה ספציפית, אבל למפתחים יש כמה אפשרויות שעומדות בדרישות האלה.
לדוגמה, אפשר למיין את רשימת החיישנים באמצעות שילוב של המאפיינים הקבועים של כל חיישן, כמו ספק, דגם וסוג חיישן. אפשרות אחרת מבוססת על העובדה שקבוצת החיישנים הסטטיים של המכשיר קבועה בחומרה, ולכן ה-HAL צריך לדעת מתי כל החיישנים הצפויים השלימו את האיפוס לפני שהוא חוזר מ-getSensorsList()
או מ-getSensorsList_2_1()
. אפשר לקמפל את רשימת החיישנים הצפויים לקובץ ה-HAL הבינארי או לאחסן אותה בקובץ תצורה במערכת הקבצים, וניתן להשתמש בסדר ההופעה כדי להפיק כינויים יציבים. הפתרון הטוב ביותר תלוי בפרטי ההטמעה הספציפיים של ה-HAL, אבל הדרישה העיקרית היא שהמזהים של החיישנים לא ישתנו במהלך הפעלות מחדש של ה-HAL.
הגדרת חיישנים
לפני שמפעילים חיישן, צריך להגדיר לו תקופת דגימה וזמן אחזור מקסימלי לדיווח באמצעות הפונקציה batch()
.
תמיד אפשר להגדיר מחדש את החיישן באמצעות batch()
בלי לאבד את נתוני החיישנים.
תקופת הדגימה
לתקופה לדגימה יש משמעות שונה בהתאם לסוג החיישן שמוגדר:
- רציף: אירועי חיישנים נוצרים בקצב רציף.
- בזמן שינוי: האירועים נוצרים לא מהר יותר ממחזור הדגימה, ויכול להיות שהם נוצרים בקצב איטי יותר ממחזור הדגימה אם הערך שנמדד לא משתנה.
- חד-פעמית: תקופת הדגימה מתעלמת.
- מיוחד: פרטים נוספים זמינים במאמר סוגי חיישנים.
למידע על האינטראקציה בין תקופת הדגימה למצבי הדיווח של חיישן, ראו מצבי דיווח.
זמן אחזור מקסימלי לדיווח
זמן האחזור המקסימלי לדיווח קובע את הזמן המקסימלי בננו-שניות שבו אירועים יכולים להתעכב ולאחסן ב-FIFO של החומרה לפני שהם נכתבים ל-Event FMQ דרך HAL בזמן שה-SoC לא פעיל.
ערך אפס מציין שצריך לדווח על האירועים ברגע שהם נמדדים, על ידי דילוג על ה-FIFO לגמרי או על ידי ריקון ה-FIFO ברגע שאירוע אחד מהחיישן נמצא ב-FIFO.
לדוגמה, תאוצה שמופעל ב-50 Hz עם זמן אחזור מקסימלי לדיווח של אפס מפעילה הפסקות 50 פעמים בשנייה כש-SoC פעיל.
כשזמן האחזור המקסימלי לדיווח גדול מאפס, אין צורך לדווח על אירועי חיישנים ברגע שהם מזוהים. אפשר לאחסן אירועים באופן זמני ב-FIFO של החומרה ולדווח עליהם בכמה פעמים, כל עוד אין עיכוב של יותר מזמן האחזור המקסימלי לדיווח. כל האירועים מאז האצווה הקודמת מתועדים ומוחזרים בבת אחת. כך מופחת מספר ההפרעות שנשלחות ל-SoC, ומאפשר ל-SoC לעבור למצב צריכת אנרגיה נמוכה יותר בזמן שהחיישן מתעד את הנתונים ומקבץ אותם.
לכל אירוע יש חותמת זמן משויכת. עיכוב של הזמן שבו מתבצע הדיווח על אירוע לא יכול להשפיע על חותמת הזמן של האירוע. חותמת הזמן חייבת להיות מדויקת ותואמת לשעה שבה האירוע התרחש פיזית, ולא לשעה שבה הוא דווח.
מידע נוסף ודרישות נוספות לגבי דיווח על אירועי חיישנים עם זמן אחזור מקסימלי של דיווח שאינו אפס זמינים במאמר אוסף נתונים.
הפעלת חיישנים
המסגרת מפעילה ומשביתה חיישנים באמצעות הפונקציה activate()
.
לפני שמפעילים חיישן, צריך להגדיר אותו באמצעות batch()
.
אחרי שהחיישן יושבת, אסור לכתוב אירועי חיישן נוספים מהחיישן הזה ל-Event FMQ.
חיישני שטיפה
אם חיישן מוגדר לאסוף נתונים בקבוצות, אפשר להפעיל את flush()
כדי לאלץ את המסגרת לבצע שטיפה מיידית של אירועי חיישנים שנאספו בקבוצות. כתוצאה מכך, אירועי החיישן שנצברו בקבוצה עבור ה-handle של החיישן שצוין נכתבים באופן מיידי ב-Event FMQ. HAL של החיישנים חייב לצרף אירוע של סיום שטיפה לסוף אירועי החיישנים שנכתבים כתוצאה מהקריאה ל-flush()
.
הניקוי מתבצע באופן אסינכרוני (כלומר, הפונקציה חייבת לחזור באופן מיידי). אם ההטמעה משתמשת ב-FIFO יחיד למספר חיישנים, ה-FIFO הזה יתרוקן והאירוע 'סיום הריקון' יתווסף רק לחיישן שצוין.
אם לחיישן שצוין אין FIFO (לא ניתן לבצע אגירת נתונים), או אם ה-FIFO היה ריק בזמן הקריאה, הפונקציה flush()
עדיין חייבת להצליח ולשלוח אירוע flush complete לחיישן הזה. הכלל הזה חל על כל החיישנים, מלבד חיישנים מסוג 'צילום אחד'.
אם flush()
נקרא לחיישן של זריקה אחת, flush()
חייב להחזיר את הערך BAD_VALUE
ולא ליצור אירוע של סיום שטיפה.
כתיבת אירועי חיישנים ב-FMQ
Sensors HAL משתמש ב-Event FMQ כדי לדחוף אירועי חיישנים למסגרת החיישנים של Android.
ה-FMQ של האירועים הוא FMQ מסונכרן, כלומר כל ניסיון לכתוב ל-FMQ יותר אירועים ממה שאפשר להכניס למרחב הזמין גורם לכתיבה שנכשלת. במקרה כזה, תכונת ה-HAL צריכה לקבוע אם לכתוב את קבוצת האירועים הנוכחית כשתי קבוצות קטנות יותר של אירועים, או לכתוב את כל האירועים יחד כשיש מספיק מקום.
כשממשק ה-HAL של החיישנים כתב את המספר הרצוי של אירועי חיישנים
ל-Event FMQ, ה-HAL של החיישנים חייב להודיע למסגרת שהאירועים מוכנים על ידי
כתיבת הביט EventQueueFlagBits::READ_AND_PROCESS
לפונקציה EventFlag::wake
של ה-Event FMQ. אפשר ליצור את EventFlag מ-Event FMQ באמצעות EventFlag::createEventFlag
והפונקציה getEventFlagWord()
של Event FMQ.
Sensors HAL 2.0/2.1 תומך גם ב-write
וגם ב-writeBlocking
ב-Event FMQ.
ההטמעה שמוגדרת כברירת מחדל מספקת הנחיה לשימוש ב-write
. אם משתמשים בפונקציה writeBlocking
, צריך להגדיר את הדגל readNotification
לערך EventQueueFlagBits::EVENTS_READ
, שנקבע על ידי ה-framework כשמקריאים אירועים מ-Event FMQ. צריך להגדיר את הדגל של התראת הכתיבה לערך EventQueueFlagBits::READ_AND_PROCESS
, כדי להודיע למסגרת שהאירועים נכתבו ב-Event FMQ.
אירועי WAKE_UP
אירועי WAKE_UP
הם אירועי חיישן שגורמים למעבד האפליקציות (AP) להתעורר ולטפל באירוע באופן מיידי. בכל פעם שאירוע WAKE_UP
נכתב ל-FMQ של האירוע, ממשק ה-HAL של החיישנים חייב לקבע נעילה של מצב שינה כדי להבטיח שהמערכת תישאר במצב שינה עד שה-framework יוכל לטפל באירוע. לאחר קבלת אירוע WAKE_UP
, ה-framework מאבטח נעילה מצב שינה משלו, וכך מאפשר לחיישן HAL לשחרר את ה-wake Lock. כדי לסנכרן את האירוע שבו ה-HAL של החיישנים משחרר את נעילת ההתעוררות, משתמשים ב-Wake Lock FMQ.
ממשק ה-HAL של החיישנים חייב לקרוא את ה-FMQ מסוג Wake Lock כדי לקבוע את מספר אירועי WAKE_UP
שבהם ה-framework טיפל. תכונת ה-HAL צריכה לשחרר את ה-wake Lock של אירועי WAKE_UP
רק אם המספר הכולל של אירועי WAKE_UP
שלא טופלו הוא אפס.
לאחר טיפול באירועי חיישנים, ה-framework סופר את מספר האירועים שמסומנים כאירועי WAKE_UP
וכותב את המספר הזה בחזרה ל-FMQ מסוג Wake Lock.
המסגרת מגדירה את ההתראה WakeLockQueueFlagBits::DATA_WRITTEN
על הכתיבה ב-Wake Lock FMQ בכל פעם שהיא כותבת נתונים ב-Wake Lock FMQ.
חיישנים דינמיים
חיישנים דינמיים הם חיישנים שלא נמצאים פיזית במכשיר, אבל אפשר להשתמש בהם כקלט למכשיר, כמו גיימפאד עם מד תאוצה.
כשמחוברים חיישן דינמי, הפונקציה onDynamicSensorConnected
ב-ISensorsCallback
צריכה להיקרא HAL של החיישנים. כך המערכת תודיע למסגרת על החיישן הדינמי החדש, ותאפשר לשלוט בחיישן דרך המסגרת ולאפשר ללקוחות לצרוך את האירועים של החיישן.
באופן דומה, כשחיישן דינמי מנותק, צריך להפעיל את הפונקציה onDynamicSensorDisconnected
ב-ISensorsCallback
כדי שהמסגרת תוכל להסיר כל חיישן שלא זמין יותר.
ערוץ ישיר
ערוץ ישיר הוא שיטת הפעלה שבה אירועי חיישנים נכתבים בזיכרון ספציפי במקום ב-Event FMQ, ועוקפים את Android Sensors Framework. לקוח שמירשם ערוץ ישיר צריך לקרוא את אירועי החיישן ישירות מהזיכרון ששימש ליצירת הערוץ הישיר, ולא יקבל את אירועי החיישן דרך המסגרת. הפונקציה configDirectReport()
דומה לפונקציה batch()
במצב עבודה רגיל, והיא קובעת את ערוץ הדיווח הישיר.
הפונקציות registerDirectChannel()
ו-unregisterDirectChannel()
יוצרות או משמידות שיחה ישירה חדשה.
מצבי הפעולה
הפונקציה setOperationMode()
מאפשרת ל-framework להגדיר חיישן כדי שהמסגרת תוכל להחדיר נתוני חיישנים לחיישן. האפשרות הזו שימושית לבדיקה, במיוחד של אלגוריתמים שנמצאים מתחת למסגרת.
בדרך כלל משתמשים בפונקציה injectSensorData()
ב-HAL 2.0 ובפונקציה injectSensorsData_2_1()
ב-HAL 2.0 כדי לדחוף פרמטרים תפעוליים ל-HAL של החיישנים. אפשר גם להשתמש בפונקציה כדי להחדיר אירועי חיישן לחישן ספציפי.
אימות
כדי לאמת את ההטמעה של חיישני HAL, יש להריץ את בדיקות ה-CTS ו-VTS של החיישן.
בדיקות CTS
בדיקות 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 לחיישנים HAL 2.0 ממוקמות ב-hardware/interfaces/sensors/2.0/vts.
בדיקות VTS לחיישנים HAL 2.1 ממוקמות ב-hardware/interfaces/sensors/2.1/vts.
הבדיקות האלה מבטיחות שה-HAL של החיישנים מוטמע כראוי ושכל הדרישות ב-ISensors.hal
וב-ISensorsCallback.hal
מתקיימות.
שדרוג לחיישנים HAL 2.1 מגרסה 2.0
כשמשדרגים לחיישן HAL 2.1 מגרסה 2.0, הטמעת HAL צריכה לכלול את השיטות initialize_2_1()
, getSensorsList_2_1()
ו-injectSensorsData_2_1()
, יחד עם סוגי HAL 2.1. השיטות האלה צריכות לעמוד באותן דרישות שמפורטות למעלה ל-HAL 2.0.
מאחר ש-HAL בגרסה משנית חייב לתמוך בכל הפונקציות מ-HALs קודמים, HAL בגרסה 2.1 חייב לתמוך בהפעלה כ-HAL בגרסה 2.0. כדי להימנע מהמורכבות של תמיכה בשתי הגרסאות של HAL, מומלץ מאוד להשתמש ב-Multi-HAL 2.1.
דוגמה להטמעת חיישנים 2.1 HAL משלכם: Sensors.h
שדרוג לגרסה 1.0 של חיישנים HAL 2.0
כשמשדרגים לחיישן HAL 2.0 מגרסה 1.0, חשוב לוודא שהטמעת תקן HAL עומדת בדרישות הבאות.
אתחול HAL
צריכה להיות תמיכה בפונקציה initialize()
כדי ליצור FMQs בין המסגרת ל-HAL.
חשיפת החיישנים הזמינים
ב-Sensors HAL 2.0, הפונקציה getSensorsList()
חייבת להחזיר את אותו ערך במהלך הפעלה יחידה של המכשיר, גם במהלך הפעלות מחדש של Sensors HAL. הפונקציה getSensorsList()
נדרשת חדשה להחזיר את אותו ערך במהלך הפעלה אחת של מכשיר, גם בכל הפעלה מחדש של ממשק HAL בין חיישנים. כך המערכת תוכל לנסות ליצור מחדש את החיבורים לחיישנים אם שרת המערכת יופעל מחדש. הערך שמוחזר על ידי getSensorsList()
יכול להשתנות אחרי שהמכשיר מבצע הפעלה מחדש.
כתיבת אירועי חיישנים ב-FMQ
במקום להמתין לקריאה ל-poll()
, ב-Sensors HAL 2.0, ה-HAL של החיישנים צריך לכתוב באופן יזום אירועי חיישנים ל-Event FMQ בכל פעם שאירועי חיישנים זמינים. ה-HAL אחראי גם לכתוב את הביטים הנכונים ל-EventFlag
כדי לגרום לקריאה של FMQ בתוך המסגרת.
אירועי WAKE_UP
בחיישן HAL 1.0, טכנולוגיית HAL הצליחה לשחרר את השחרור של מצב השינה בכל אירוע של WAKE_UP
בכל קריאה עתידית אל poll()
אחרי פרסום WAKE_UP
ב-poll()
. הסיבה לכך היא שה-framework עיבד את כל אירועי החיישנים והשיג נעילה של מצב שינה, במקרה הצורך. מכיוון שב-Sensors HAL 2.0, ה-HAL כבר לא יודע מתי המסגרת עיבד אירועים שנכתבו ב-FMQ, ה-Wake Lock FMQ מאפשר למסגרת לתקשר עם ה-HAL כשהיא מטפלת באירועי WAKE_UP
.
ב-Sensors HAL 2.0, מנעול ההתעוררות שמאובטח על ידי Sensors HAL לאירועים מסוג WAKE_UP
חייב להתחיל ב-SensorsHAL_WAKEUP
.
חיישנים דינמיים
חיישנים דינמיים הוחזרו באמצעות הפונקציה poll()
ב-Sensors HAL 1.0.
לפי חיישן HAL 2.0, נדרשת קריאה ל-onDynamicSensorsConnected
ול-onDynamicSensorsDisconnected
ב-ISensorsCallback
בכל פעם שיש שינוי בחיבורים של החיישנים הדינמיים. פונקציות הקריאה החוזרת האלה זמינות כחלק מהצבען ISensorsCallback
שסופק באמצעות הפונקציה initialize()
.
מצבי פעולה
צריכה להיות תמיכה במצב DATA_INJECTION
של חיישני WAKE_UP
ב-Sensors HAL 2.0.
תמיכה במספר ממשקי HAL
ב-Sensors HAL 2.0 ו-2.1 יש תמיכה במספר ממשקי HAL באמצעות המסגרת של Sensors Multi-HAL. לפרטים נוספים לגבי הטמעה, ראו ניוד מחיישנים HAL 1.0.