פוקוס אודיו

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

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

אינטראקציות עם מיקוד

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

אינטראקציה בלעדית

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

דחיית אינטראקציה

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

אינטראקציה בו-זמנית

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

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

טיפול בשידורים חיים בו-זמניים

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

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

מטריצה של אינטראקציות

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

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

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

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

אינטראקציות עם מיקוד אודיו

איור 1. אינטראקציות של מיקוד אודיו

ב-Android 11 נוספה הגדרת משתמש חדשה שמאפשרת למשתמשים לשנות את התנהגות האינטראקציה בין הניווט לשיחות טלפון. כשהערך של android.car.KEY_AUDIO_FOCUS_NAVIGATION_REJECTED_DURING_CALL מוגדר, הוא משנה את האינטראקציה בין בקשות NAVIGATION נכנסות לקבלת המיקוד לבין בעלי המיקוד הנוכחיים CALL מבו-זמנית לדחייה. כך, אם משתמש מעדיף שלא יפריעו לו הוראות ניווט במהלך השיחה, הוא יכול להפעיל את ההגדרה הזו. הערך הזה נשמר אצל המשתמש, וניתן להגדיר אותו באופן דינמי כדי שבקשות העברת המיקוד הבאות יתייחסו לערך ההגדרה החדש.

הרשאת אודיו שניתן לדחות

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

כללים לבקשות מושהות להעברת המיקוד של האודיו

  • בקשות לא זמניות בלבד – כפי שצוין קודם, אפשר לשלוח בקשה מושהית רק לגבי מקורות לא זמניים. כך אפשר למנוע מצב שבו צליל זמני יישמע הרבה אחרי שהוא רלוונטי.
  • אפשר לדחות רק בקשה אחת בכל פעם – אם שולחים בקשה שניתן לדחות בזמן שכבר יש בקשה מושהית, הבקשה המקורית המושהית מקבלת אירוע שינוי מסוג AUDIOFOCUS_LOSS, והבקשה החדשה מקבלת תשובה סינכרונית מסוג AUDIOFOCUS_REQUEST_DELAYED.
  • בקשות שאפשר לדחות חייבות לכלול OnAudioFocusChangeListener. אחרי שהבקשה מתעכבת, המאזין משמש כדי להודיע למבקש כשהבקשה מאושרת בסופו של דבר (AUDIOFOCUS_GAIN), או אם היא נדחית מאוחר יותר (AUDIOFOCUS_LOSS).

בקשה להעברת המיקוד למועד מאוחר יותר

כדי ליצור בקשה שאפשר לעכב, משתמשים ב-AudioFocusRequest.Builder#setAcceptsDelayedFocusGain:

mMediaWithDelayedFocusListener = new MediaWithDelayedFocusListener();

mDelayedFocusRequest = new AudioFocusRequest
     .Builder(AudioManager.AUDIOFOCUS_GAIN)
     .setAudioAttributes(mMusicAudioAttrib)
     .setOnAudioFocusChangeListener(mMediaWithDelayedFocusListener)
     .setForceDucking(false)
     .setWillPauseWhenDucked(false)
     .setAcceptsDelayedFocusGain(true)
     .build();

לאחר מכן, כששולחים את הבקשה, מטפלים בתגובה AUDIOFOCUS_REQUEST_DELAYED:

int delayedFocusRequestResults = mAudioManager.requestAudioFocus(mDelayedFocusRequest);
if (delayedFocusRequestResults == AudioManager.AUDIOFOCUS_REQUEST_GRANTED) {
// start audio playback
return;
}
if (delayedFocusRequestResults == AudioManager.AUDIOFOCUS_REQUEST_DELAYED) {
     // audio playback delayed to audio focus listener
     return;
}

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

private final class MediaWithDelayedFocusListener implements
OnAudioFocusChangeListener {
       @Override
       public void onAudioFocusChange(int focusChange) {
           synchronized (mLock) {
               switch (focusChange) {
                   case AudioManager.AUDIOFOCUS_GAIN:
                        // Start focus playback
                   case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT:
                        // Pause media transiently
                   case AudioManager.AUDIOFOCUS_LOSS:
                        // Stop media

ניהול התמקדות בכמה אזורים

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

בכל האפליקציות, ניהול המיקוד מתבצע באופן אוטומטי על ידי CarAudioService. אזור האודיו של בקשת התמקדות נקבע על סמך הערך המשויך של UserId או UID. פרטים נוספים זמינים במאמר ניתוב אודיו.

שליחת בקשה לקבלת אודיו ממספר תחומים בו-זמנית

אם אפליקציה רוצה להשמיע אודיו בכמה תחומים בו-זמנית, היא צריכה לבקש להתמקד בכל תחום על ידי הכללת AUDIOFOCUS_EXTRA_REQUEST_ZONE_ID בחבילה:

// Create attribute with bundle and AUDIOFOCUS_EXTRA_REQUEST_ZONE_ID
Bundle bundle = new Bundle();
bundle.putInt(CarAudioManager.AUDIOFOCUS_EXTRA_REQUEST_ZONE_ID,
               zoneId);

AudioAttributes attributesWithZone = new AudioAttributes.Builder()
     .setUsage(AudioAttributes.USAGE_MEDIA)
     .addBundle(bundle)
     .build();

// Create focus request using built attributesWithZone

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

HAL Audio Focus

החל מ-Android 11, ה-HAL מופעל עכשיו כדי לבקש להתמקד בשידורים חיצוניים. ממשקי ה-API האלה הם אופציונליים, אבל מומלץ מאוד להשתמש בהם כדי לאפשר לצלילים חיצוניים להשתלב טוב יותר בסביבת Android ולספק חוויית משתמש חלקה יותר.

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

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

AudioControl@2.0

בגרסה 2.0 של AudioControl HAL נוספו כמה ממשקי API חדשים:

API המטרה
IAudioControl#registerFocusListener רישום מופע של IFocusListener ב-HAL של AudioControl. המאזין הזה מאפשר ל-HAL לבקש את המיקוד באודיו ולבטל אותו. ה-HAL אמור לספק מופע של ICloseHandle ש-Android תשתמש בו כדי לבטל את הרישום של המאזין.
IAudioControl#onAudioFocusChange מודיע ל-HAL על שינויים בסטטוס כדי להתמקד בבקשות שה-HAL שולח דרך IFocusListener. זה כולל תגובות לבקשות ראשוניות להתמקד בנושא.
IFocusListener#requestAudioFocus הבקשות מתמקדות בשם HAL לשימוש ספציפי, למזהה תחום ולסוג של התמקדות.
IFocusListener#abandonAudioFocus ביטול בקשות קיימות של HAL focus עבור מזהה השימוש ומזהה האזור שצוינו.

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

מלבד registerFocusListener, כל הבקשות האלה הן oneway כדי לוודא שמערכת Android לא מעכבת את HAL בזמן עיבוד בקשת התמקדות. ה-HAL לא צריך להמתין לקבלת המיקוד לפני השמעת צלילים קריטיים לבטיחות. ה-HAL יכול להאזין לשינויים בריכוז האודיו דרך IAudioControl#onAudioFocusChange ולהגיב להם, אבל מומלץ לעשות זאת רק במקרים הרלוונטיים.