הערות תכנות של OpenSL ES

אזהרה: OpenSL ES הוצא משימוש. המפתחים צריכים להשתמש בספריית הקוד הפתוח Oboe, שזמינה ב-GitHub. Oboe הוא wrapper של C++ שמספק API שדומה מאוד ל-AAudio. כש-AAudio זמין, Oboe קורא ל-AAudio, ואם AAudio לא זמין, הוא עובר ל-OpenSL ES.

ההערות בקטע הזה הן תוספת למפרט OpenSL ES 1.0.1.

אובייקטים והפעלת ממשק

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

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

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

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

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

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

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

שליפה מראש של נגן אודיו

בנגן אודיו עם מקור נתונים URI, הפקודה Object::Realize מקצה משאבים אבל לא מתחברת למקור הנתונים (להכין) או מתחיל בשליפה מראש של הנתונים. האירועים האלה מתרחשים אחרי שמצב השחקן מוגדר ל-SL_PLAYSTATE_PAUSED או ל-SL_PLAYSTATE_PLAYING.

יכול להיות שחלק מהמידע עדיין לא יהיה ידוע עד לשלב מאוחר יחסית בסדר הזה. באופן ספציפי, הפונקציה Player::GetDuration מחזירה את הערך SL_TIME_UNKNOWN ו-MuteSolo::GetChannelCount מחזירה בהצלחה עם מספר הערוצים אפס או עם תוצאת השגיאה SL_RESULT_PRECONDITIONS_VIOLATED. ממשקי ה-API האלה מחזירים את הערכים המתאימים ברגע שהם ידועים.

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

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

גם הממשק של סטטוס השליפה מראש (prefetch) שימושי לזיהוי שגיאות. צריך לרשום קריאה חוזרת ולהפעיל לפחות את האירועים SL_PREFETCHEVENT_FILLLEVELCHANGE ו-SL_PREFETCHEVENT_STATUSCHANGE. אם שני האירועים האלה נשלחים בו-זמנית, ו-PrefetchStatus::GetFillLevel מדווח על רמה אפס ו-PrefetchStatus::GetPrefetchStatus מדווח על SL_PREFETCHSTATUS_UNDERFLOW, סימן שיש שגיאה שלא ניתן לשחזר במקורות הנתונים. למשל, אי אפשר להתחבר למקור הנתונים כי שם הקובץ המקומי לא קיים או שה-URI של הרשת לא תקין.

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

לסיכום, רצף הקוד המומלץ הוא:

  1. Engine::CreateAudioPlayer
  2. Object:Realize
  3. Object::GetInterface למשך SL_IID_PREFETCHSTATUS
  4. PrefetchStatus::SetCallbackEventsMask
  5. PrefetchStatus::SetFillUpdatePeriod
  6. PrefetchStatus::RegisterCallback
  7. Object::GetInterface למשך SL_IID_PLAY
  8. Play::SetPlayState עד SL_PLAYSTATE_PAUSED, או SL_PLAYSTATE_PLAYING

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

להרוס

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

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

ההטמעה של Android OpenSL ES לא מזהה שימוש שגוי בממשקים כאלה. המשך השימוש בממשקים כאלה אחרי שהאובייקט נהרס עלול לגרום לקריסת האפליקציה או להתנהגות בלתי צפויה.

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

תנועה אופקית בסטריאו

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

קריאות חוזרות (callbacks) ושרשור

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

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

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

חשוב לזכור שההיפך הוא בטוח: שרשור של אפליקציה ל-Android שנכנסו ל-JNI יכול לקרוא ישירות לממשקי API של OpenSL ES, כולל אלה שחוסמים. עם זאת, לא מומלץ לבצע קריאות חסימה מה-thread הראשי, כי הן עלולות לגרום לשגיאה מסוג Application Not Responding (ANR).

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

לא מובטח שלשרשור שבו פועל ה-handler של ה-callback תהיה אותה זהות בקריאות שונות. לכן, אל תסמכו על כך שהערך של pthread_t שמוחזר על ידי pthread_self() או הערך של pid_t שמוחזר על ידי gettid() יהיה עקבי בין קריאות. מאותה סיבה, אל תשתמשו בממשקי ה-API של האחסון המקומי של השרשור (TLS), כמו pthread_setspecific() ו-pthread_getspecific(), מתוך פונקציית קריאה חוזרת (callback).

ההטמעה מבטיחה שלא יתרחשו קריאות חזרה (callbacks) בו-זמנית מאותו סוג, לאותו אובייקט. עם זאת, אפשר להפעיל קריאות חזרה (callbacks) בו-זמנית מסוגים שונים לאותו אובייקט בשרשור (thread) שונה.

ביצועים

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

אחת מהשיפורים האלה היא תמיכה בזמן אחזור קצר יותר של פלט האודיו. היסודות להפחתת זמן האחזור של הפלט נכללו לראשונה ב-Android 4.1 (API ברמה 16), ולאחר מכן התקדמות נוספת התרחשה ב-Android 4.2 (API ברמה 17). השיפורים האלה זמינים דרך OpenSL ES להטמעות במכשירים שמצהירים על זכויות יוצרים בתכונה android.hardware.audio.low_latency. אם המכשיר לא תומך בתכונה הזו אבל תומך ב-Android 2.3 (רמת API 9) ואילך, עדיין תוכלו להשתמש בממשקי ה-API של OpenSL ES, אבל זמן האחזור של הפלט עשוי להיות ארוך יותר. משתמשים בנתיב זמן האחזור של הפלט רק אם האפליקציה מבקשת גודל של מאגר נתונים זמני וקצב דגימה שתואמים להגדרות הפלט המקוריות של המכשיר. הפרמטרים האלה ספציפיים למכשיר, וצריך לקבל אותם כפי שמתואר בהמשך.

החל מגרסה 4.2 של Android‏ (רמת API 17), אפליקציה יכולה לשלוח שאילתה לגבי קצב הדגימה הטבעי או האופטימלי של הפלט וגודלו של מאגר הנתונים (buffer) של מקור הפלט הראשי של המכשיר. בשילוב עם בדיקת התכונה שצוינה קודם, האפליקציה יכולה עכשיו להגדיר את עצמה בצורה מתאימה כדי לספק פלט עם זמן אחזור קצר יותר במכשירים שמצהירים על תמיכה.

ב-Android מגרסה 4.2 (רמת API 17) ומטה, נדרש מספר מאגרים של שניים או יותר כדי לקצר את זמן האחזור. החל מגרסה 4.3 של Android‏ (רמת API 18), מספיק ספירה של מאגר אחד כדי לקצר את זמן האחזור.

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

הסדר המומלץ הוא:

  1. צריך לבדוק אם יש API ברמה 9 ואילך כדי לאשר את השימוש ב-OpenSL ES.
  2. אפשר לבדוק אם התכונה android.hardware.audio.low_latency זמינה באמצעות קוד כמו זה:

    Kotlin

    import android.content.pm.PackageManager
    ...
    val pm: PackageManager = context.packageManager
    val claimsFeature: Boolean = pm.hasSystemFeature(PackageManager.FEATURE_AUDIO_LOW_LATENCY)

    Java

    import android.content.pm.PackageManager;
    ...
    PackageManager pm = getContext().getPackageManager();
    boolean claimsFeature = pm.hasSystemFeature(PackageManager.FEATURE_AUDIO_LOW_LATENCY);
  3. בודקים אם רמת ה-API היא 17 ואילך כדי לוודא את השימוש ב-android.media.AudioManager.getProperty().
  4. כדי לקבל את קצב הדגימה ואת גודל המאגר של הפלט המקורי או האופטימלי של מקור הפלט הראשי של המכשיר, משתמשים בקוד כמו זה:

    Kotlin

    import android.media.AudioManager
    ...
    val am = getSystemService(Context.AUDIO_SERVICE) as AudioManager
    val sampleRate: String = am.getProperty(AudioManager.PROPERTY_OUTPUT_SAMPLE_RATE)
    val framesPerBuffer: String = am.getProperty(AudioManager.PROPERTY_OUTPUT_FRAMES_PER_BUFFER)

    Java

    import android.media.AudioManager;
    ...
    AudioManager am = (AudioManager) getSystemService(Context.AUDIO_SERVICE);
    String sampleRate = am.getProperty(AudioManager.PROPERTY_OUTPUT_SAMPLE_RATE);
    String framesPerBuffer = am.getProperty(AudioManager.PROPERTY_OUTPUT_FRAMES_PER_BUFFER);
    שימו לב ש-sampleRate ו-framesPerBuffer הם מחרוזות. קודם בודקים אם הערך הוא null, ואז ממירים אותו ל-int באמצעות Integer.parseInt().
  5. עכשיו משתמשים ב-OpenSL ES כדי ליצור AudioPlayer עם מאתר נתונים של תור מאגר PCM.

הערה: אפשר להשתמש באפליקציית הבדיקה של שטח האחסון הזמני של האודיו כדי לקבוע את הגודל של מאגר הנתונים הזמני ואת קצב הדגימה באפליקציות אודיו של OpenSL ES במכשיר האודיו. אפשר גם להיכנס ל-GitHub כדי לראות דוגמאות ל- audio-buffer-size.

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

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

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

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

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

החל מגרסה 5.0 של Android (רמת API‏ 21), יש תמיכה בקלט אודיו עם זמן אחזור קצר יותר במכשירים נבחרים. כדי לנצל את היתרונות של התכונה הזו, תחילה ודאו שקיימת פלט עם זמן אחזור קצר יותר כמו שמתואר למעלה. היכולת להפיק פלט עם זמן אחזור נמוך יותר היא תנאי מוקדם לשימוש בתכונה 'קלט עם זמן אחזור נמוך יותר'. לאחר מכן יוצרים אודיו-ריקורדר עם אותו קצב דגימה ואותו גודל של מאגר נתונים זמני שישמשו את הפלט. ממשקי OpenSL ES לאפקטים של קלט מחליפים את נתיב זמן האחזור הנמוך יותר. כדי לקצר את זמן האחזור, צריך להשתמש בהגדרה המוגדרת מראש לתיעוד SL_ANDROID_RECORDING_PRESET_VOICE_RECOGNITION. ההגדרה הזו משביתה עיבוד אותות דיגיטלי ספציפי למכשיר שעלול להוסיף זמן אחזור לנתיב הקלט. מידע נוסף על הגדרות קבועות מראש להקלטה זמין בקטע ממשק ההגדרות של Android שלמעלה.

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

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

מצבי ביצועים

החל מגרסה 7.1 של Android‏ (רמת API 25), נוספה ל-OpenSL ES אפשרות לציין מצב ביצועים לנתיב האודיו. האפשרויות הן:

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

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

כדי להגדיר את מצב הביצועים, צריך להפעיל את SetConfiguration באמצעות ממשק התצורה של Android, כפי שמתואר בהמשך:

  // Obtain the Android configuration interface using a previously configured SLObjectItf.
  SLAndroidConfigurationItf configItf = nullptr;
  (*objItf)->GetInterface(objItf, SL_IID_ANDROIDCONFIGURATION, &configItf);

  // Set the performance mode.
  SLuint32 performanceMode = SL_ANDROID_PERFORMANCE_NONE;
    result = (*configItf)->SetConfiguration(configItf, SL_ANDROID_KEY_PERFORMANCE_MODE,
                                                     &performanceMode, sizeof(performanceMode));

אבטחה והרשאות

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

אפליקציות שמשתמשות ב-OpenSL ES צריכות לבקש את ההרשאות הנדרשות לממשקי API דומים שאינם ילידים. לדוגמה, אם האפליקציה מקליטה אודיו, היא צריכה את ההרשאה android.permission.RECORD_AUDIO. אפליקציות שמשתמשות באפקטים קוליים צריכות את android.permission.MODIFY_AUDIO_SETTINGS. אפליקציות שמפעילות משאבי URI ברשת צריכות את android.permission.NETWORK. מידע נוסף זמין במאמר עבודה עם הרשאות מערכת.

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