بدءًا من Android 17 (المستوى 37 من واجهة برمجة التطبيقات) الذي تم تحديثه من خلال "تحديثات نظام Google Play" (وحدات APEX الرئيسية)، يقدّم نظام Android برامج ترميز صوتية داخل العملية لتنسيقات الصوت المضغوطة، بما في ذلك Opus وAAC.
من خلال تشغيل برامج ترميز الصوت مباشرةً ضمن عملية التطبيق بدلاً من تشغيلها في عملية نظام منفصلة في بيئة معزولة، يمكن للتطبيقات تقليل وقت استجابة ترميز الصوت بنسبة 40%تقريبًا، وتقليل استخدام وحدة المعالجة المركزية، وإطالة عمر البطارية أثناء تشغيل الوسائط بشكل مستمر، وإرسال الرسائل الصوتية، وممارسة الألعاب.
فك التشفير خارج العملية مقابل فك التشفير داخل العملية
في السابق، كان نظام التشغيل Android يشغّل جميع برامج فك ترميز الوسائط الخاصة بالأنظمة الأساسية داخل برنامج خفي مخصّص ومحمي بنظام الحماية المعزولة (mediaswcodec) لحماية التطبيقات ونظام التشغيل من تدفقات بيانات الوسائط المشوّهة. ومع ذلك، يؤدي وضع التطبيقات في بيئة معزولة خارج العملية إلى زيادة ملحوظة في الأعباء على الأداء:
- وقت استجابة الاتصال بين العمليات (IPC): يتطلّب كل إطار إدخال صوتي يتم وضعه في قائمة الانتظار في برنامج الترميز وكل مخزن مؤقت لبيانات PCM الصوتية التي يتم فك ترميزها إجراء تسلسل للاتصال بين العمليات باستخدام Binder والتبديل بين السياقات.
- العبء الزائد للذاكرة المشتركة: تتطلّب عمليات تسليم المخزن المؤقت بين العمليات مزامنة الذاكرة المشتركة (
C2Buffer) وإدارة ذاكرة التخزين المؤقت. - تضارب الجدولة: عند ارتفاع معدّل استخدام وحدة المعالجة المركزية، يمكن أن يؤدي تضارب جدولة سلاسل التعليمات بين عملية التطبيق و
mediaswcodecإلى نقص في المخزن المؤقت وتقطُّع الصوت في مسارات الصوت ذات زمن الاستجابة المنخفض (مثل محطات العمل الصوتية الرقمية واتصالات بروتوكول نقل الصوت عبر الإنترنت والألعاب التفاعلية).
يؤدي تشغيل برامج فك ترميز الوسائط داخل العملية إلى حلّ المشاكل التالية المتعلقة بأداء التطبيق:
- إزالة وقت الاستجابة في عملية الاتصال بين العمليات (IPC): يتم تجاوز معاملات IPC في Binder وعمليات تبديل السياق، ما يؤدي إلى تقليل وقت استجابة فك الترميز من البداية إلى النهاية بنسبة %40 تقريبًا.
- تقليل الحمل الزائد للمخزن المؤقت: يتم تمرير مخازن الصوت المؤقتة مباشرةً ضمن مساحة ذاكرة التطبيق المحلية بدون الحاجة إلى تعيينات ذاكرة مشتركة بين العمليات.
- منع حدوث تشويش في الجدولة: يتم تنفيذ عملية فك الترميز على سلاسل التعليمات ذات الأولوية الخاصة بالتطبيق (مثل سلاسل التعليمات في AAudio أو Oboe في الوقت الفعلي)، ما يمنع حدوث انقطاعات في الصوت ونقص في المخزن المؤقت عند زيادة الحمل على النظام.
أمان الذاكرة باستخدام Rust
في السابق، كان نظام التشغيل Android يعزل برامج فك الترميز في عملية منفصلة (mediaswcodec) لأنّ برامج فك الترميز تعالج تدفقات البتات غير الموثوق بها من ملفات الوسائط الخارجية، ما يجعل عمليات استغلال تلف الذاكرة (مثل تجاوز سعة المخزن المؤقت) مصدر قلق أمني كبير.
يمكن لنظام التشغيل Android نقل برامج الترميز مباشرةً إلى عملية تطبيق الاتصال بأمان من خلال تنفيذها بلغات آمنة من حيث الذاكرة، مثل Rust، أو عزلها باستخدام ميزة "العزل الخفيف للأخطاء" (LFI).
للتخلص بأمان من النفقات العامة لوضع الحماية خارج العملية في AAC
(c2.android.inproc.aac.decoder)، يوفّر نظام التشغيل Android برنامج ترميز تم تنفيذه في
Rust الخالص أو محميًا بواسطة أدوات واجهة الوظائف الخارجية (FFI) الآمنة في Rust.
يُعدّ Rust آمنًا لفك الترميز داخل العملية للأسباب التالية:
- أمان الذاكرة في وقت الترجمة البرمجية: يضمن نظام الملكية والاقتراض والتحقّق من مدة البقاء الصارم في Rust عدم إمكانية الوصول إلى الذاكرة بعد تحريرها أو تعديلها أثناء مشاركتها.
- إزالة الثغرات الأمنية الشائعة: تمنع لغة Rust تمامًا تجاوز سعة المخزن المؤقت في الذاكرة المخصّصة، وتجاوز سعة المخزن المؤقت في الذاكرة المكدّسة، وعمليات القراءة والكتابة خارج حدود المصفوفة، وأخطاء الاستخدام بعد التحرير، وثغرات التحرير المزدوج في وقت الترجمة.
- عدم فرض ضريبة على وضع الحماية في وقت التشغيل: بما أنّ سلامة الذاكرة يتم إثباتها رياضيًا في وقت الترجمة البرمجية والتحقّق منها من خلال التحقّق من الحدود، يتم تنفيذ برامج فك الترميز في Rust بالسرعة الأصلية داخل عملية التطبيق بدون الحاجة إلى عمليات نقل جدول الصفحات أو سياقات وضع الحماية للأجهزة.
أمان الذاكرة باستخدام ميزة "عزل الأخطاء البسيط" (LFI)
بالنسبة إلى برامج ترميز الصوت المعقّدة القديمة بلغة C/C++ التي لم تتم إعادة كتابتها بعد بلغة Rust، وتحديدًا برنامج ترميز Opus (c2.android.inproc.opus.decoder
الذي يعمل بواسطة libopus)، يوفّر نظام التشغيل Android أمانًا داخل العملية باستخدام
ميزة "عزل الأخطاء البسيط" (LFI).
يُعدّ LFI آمنًا لبرامج الترميز C/C++ للأسباب التالية:
- وضع الحماية الخطي للذاكرة المحمي بالأجهزة: يجمع LFI مكتبة برامج الترميز C/C++ في رمز آلة خطي مشتق من WebAssembly ومحصور في فتحة ذاكرة خطية محمية بالأجهزة بسعة 4 غيغابايت.
- مفاتيح حماية الذاكرة في نظام التشغيل Linux (
pku): تستخدم ميزة LFI مفاتيح حماية الذاكرة في نظام التشغيل Linux (pku/pkey_mprotect) لتقسيم أذونات مساحة العناوين. لا يمكن للمكتبة المعزولة قراءة الذاكرة أو الكتابة فيها خارج مساحة الحماية المخصّصة لها والتي تبلغ 4 غيغابايت. - اعتراض طلبات النظام: لا يُسمح بأي طلبات نظام لنظام التشغيل المضيف (مثل إدخال/إخراج الملفات أو الوصول إلى الشبكة أو التحكّم في العمليات) داخل وضع الحماية. يتم توجيه أي استدعاء لمكتبة غير متوافقة إلى الحد الأدنى من وحدات الترميز الوهمية (
lfi_stubs.c). - استرداد البيانات بأمان عند حدوث خطأ: إذا حاولت حزمة بيانات Opus غير صالحة أو ضارة إجراء قراءة أو كتابة خارج النطاق داخل وضع الحماية الخطي، سيتمكن وقت تشغيل LFI من رصد الخطأ بأمان في مساحة المستخدم وإرجاع خطأ
C2_CORRUPTEDفي فك الترميز بدون إيقاف التطبيق المضيف.
البُنى والأجهزة المتوافقة مع LFI
لا تتطلّب ميزة LFI تعليمات مخصّصة لأجهزة وحدة المعالجة المركزية، وهي متوافقة مع بنية معالجات ARM64 (aarch64) وx86_64:
- أجهزة ARM64 (
aarch64): تعمل معظم الأجهزة الجوّالة المخصّصة للمستهلكين، بما في ذلك الهواتف الجوّالة (مثل أجهزة Pixel التي تتضمّن أنظمة على شرائح Google Tensor وQualcomm Snapdragon وMediaTek)، والأجهزة القابلة للطي، والأجهزة اللوحية، ووحدات المعلومات والترفيه في السيارات، بمعالِجات ARM64 وتتوافق مع برامج فك الترميز داخل العملية في LFI. - أجهزة x86_64: تعمل محاكيات Android على محطات عمل المطوّرين المزوّدة بوحدات معالجة مركزية من Intel أو AMD، كما أنّ أجهزة Chromebook التي تعمل بنظام التشغيل ChromeOS وتستخدم تطبيقات Android من خلال ARC (بيئة تشغيل Android على ChromeOS) تعمل على بنية x86_64 وتتيح استخدام وضع الحماية LFI.
برامج ترميز الصوت المتاحة أثناء المعالجة
| صيغة الصوت | نوع MIME | اسم المكوّن داخل العملية | التنفيذ |
|---|---|---|---|
| Opus | audio/opus |
c2.android.inproc.opus.decoder |
C/C++ libopus مع LFI |
| AAC | audio/mp4a-latm |
c2.android.inproc.aac.decoder |
Rust الآمنة من حيث الذاكرة (C2ApexAacDec) |
- ترميز AAC (
c2.android.inproc.aac.decoder): يتعامل مع بث ADTS (مع تحليل ديناميكي لرأس ADTS) ووحدات الوصول (AU) الأولية مع تتبُّع دقيق للطابع الزمني للعرض (لا تتم معالجة وحدة وصول ذات إطار واحد إلا في حالةAAC_PACKAGING_RAW). - Opus (
c2.android.inproc.opus.decoder): يفك ترميز حِزم حاوية Ogg Opus أو Matroska العادية مع إعادة أخذ العينات داخليًا إلى 48 كيلو هرتز PCM.
تفعيل أدوات فك الترميز أثناء المعالجة
في الوقت الحالي، لا تكون برامج الترميز التي تتم معالجتها هي الإعداد التلقائي للنظام عند الاتصال
MediaCodec.createDecoderByType().
تحتفظ المنصة بأولوية أعلى لاختيار برنامج الترميز 2.0 مع برامج الترميز القديمة التي تعمل خارج العملية أثناء عملية التحقّق من المنظومة المتكاملة.
للاستفادة من أداة فك الترميز أثناء المعالجة اليوم لتشغيل المحتوى أو اختباره بزمن استجابة منخفض،
يجب إنشاء مثيل لبرنامج الترميز بشكل صريح حسب اسم المكوّن باستخدام
MediaCodec.createByCodecName().
إنشاء مثيل حسب اسم المكوّن
يوضّح المثال التالي كيفية إنشاء مثيل بشكل صريح لبرنامج فك ترميز AAC داخل العملية، مع الرجوع إلى برنامج فك الترميز التلقائي إذا لم يكن المكوّن داخل العملية متاحًا على الجهاز:
Kotlin
val INPROC_AAC_CODEC = "c2.android.inproc.aac.decoder" fun createAudioDecoder(): MediaCodec { return try { // Attempt to opt in to the low-latency in-process AAC decoder MediaCodec.createByCodecName(INPROC_AAC_CODEC) } catch (e: IllegalArgumentException) { // Fall back to the default platform AAC decoder MediaCodec.createDecoderByType(MediaFormat.MIMETYPE_AUDIO_AAC) } }
Java
private static final String INPROC_AAC_CODEC = "c2.android.inproc.aac.decoder"; public MediaCodec createAudioDecoder() throws IOException { try { // Attempt to opt in to the low-latency in-process AAC decoder return MediaCodec.createByCodecName(INPROC_AAC_CODEC); } catch (IllegalArgumentException e) { // Fall back to the default platform AAC decoder return MediaCodec.createDecoderByType(MediaFormat.MIMETYPE_AUDIO_AAC); } }
ضبط Jetpack Media3 أو ExoPlayer
إذا كان تطبيقك يستخدم Jetpack Media3 أو ExoPlayer، يمكنك إنشاء MediaCodecSelector مخصّص لتحديد أولوية برامج الترميز أثناء المعالجة عندما تكون متاحة على الجهاز:
Kotlin
class InprocPreferredMediaCodecSelector : MediaCodecSelector { override fun getDecoderInfos( mimeType: String, requiresSecureDecoder: Boolean, requiresTunnelingDecoder: Boolean ): List<MediaCodecInfo> { val defaultInfos = MediaCodecUtil.getDecoderInfos( mimeType, requiresSecureDecoder, requiresTunnelingDecoder ) val inprocNames = setOf( "c2.android.inproc.opus.decoder", "c2.android.inproc.aac.decoder" ) // Sort in-process decoders to the top of the selection list return defaultInfos.sortedByDescending { it.name in inprocNames } } }
Java
public class InprocPreferredMediaCodecSelector implements MediaCodecSelector { private static final Set<String> INPROC_NAMES = new HashSet<>(Arrays.asList( "c2.android.inproc.opus.decoder", "c2.android.inproc.aac.decoder" )); @Override public List<MediaCodecInfo> getDecoderInfos( String mimeType, boolean requiresSecureDecoder, boolean requiresTunnelingDecoder) throws MediaCodecUtil.DecoderQueryException { List<MediaCodecInfo> defaultInfos = new ArrayList<>( MediaCodecUtil.getDecoderInfos(mimeType, requiresSecureDecoder, requiresTunnelingDecoder) ); // Sort in-process decoders to the top of the selection list defaultInfos.sort((a, b) -> { boolean aInproc = INPROC_NAMES.contains(a.name); boolean bInproc = INPROC_NAMES.contains(b.name); return Boolean.compare(bInproc, aInproc); }); return defaultInfos; } }
خريطة الطريق إلى برامج الترميز التلقائية للنظام
يستعد نظام التشغيل Android حاليًا لنقل أدوات فك الترميز الآمنة من حيث الذاكرة إلى أدوات فك الترميز التلقائية للنظام لأنواع MIME الصوتية المعنية في إصدارات النظام الأساسي القادمة (بدءًا من Opus LFI وRust AAC في Android 18 أو تحديثات وحدة Mainline القادمة).
بعد أن يصبح برنامج الترميز أثناء المعالجة هو مكوّن Codec 2.0 التلقائي لنوع MIME الخاص به، سيتم تلقائيًا توجيه الطلبات العادية إلى MediaCodec.createDecoderByType(...) من خلال التنفيذ أثناء المعالجة، ما يؤدي إلى تقليل وقت استجابة فك الترميز بنسبة% 40 تقريبًا بدون الحاجة إلى إجراء أي تغييرات على رمز التطبيق.