باستخدام Binder IPC

تصف هذه الصفحة التغييرات التي تم إجراؤها على برنامج تشغيل الموثق في Android 8، وتوفر تفاصيل حول استخدام الموثق IPC، وتسرد سياسة SELinux المطلوبة.

التغييرات في برنامج تشغيل الموثق

بدءًا من Android 8، يتواصل الآن إطار عمل Android وHALs مع بعضهما البعض باستخدام Binder. نظرًا لأن هذا الاتصال يزيد بشكل كبير من حركة مرور الموثق، يتضمن Android 8 العديد من التحسينات المصممة للحفاظ على سرعة IPC الموثق. يجب أن يتم دمج بائعي SoC ومصنعي المعدات الأصلية مباشرة من الفروع ذات الصلة لـ android-4.4 وandroid-4.9 والإصدارات الأحدث من مشروع kernel/common .

مجالات الموثق المتعددة (السياقات)

Common-4.4 وما فوق، بما في ذلك المنبع

لتقسيم حركة الموثق بشكل واضح بين إطار العمل (المستقل عن الجهاز) وكود البائع (الخاص بالجهاز)، قدم Android 8 مفهوم سياق الموثق . يحتوي كل سياق رابط على عقدة جهاز خاصة به ومدير سياق (خدمة) خاص به. يمكنك الوصول إلى مدير السياق فقط من خلال عقدة الجهاز الذي ينتمي إليه، وعند تمرير عقدة رابط عبر سياق معين، لا يمكن الوصول إليه من نفس السياق إلا من خلال عملية أخرى، وبالتالي عزل المجالات تمامًا عن بعضها البعض. للحصول على تفاصيل حول الاستخدام، راجع vndbinder و vndservicemanager .

جمع مبعثر

Common-4.4 وما فوق، بما في ذلك المنبع

في الإصدارات السابقة من Android، تم نسخ كل جزء من البيانات في مكالمة Binder ثلاث مرات:

  • مرة واحدة لتسلسلها إلى Parcel في عملية الاستدعاء
  • مرة واحدة في برنامج تشغيل kernel لنسخ Parcel إلى العملية المستهدفة
  • مرة واحدة لإلغاء Parcel في العملية المستهدفة

يستخدم Android 8 تحسين التجميع المبعثر لتقليل عدد النسخ من 3 إلى 1. بدلاً من إجراء تسلسل للبيانات في Parcel أولاً، تظل البيانات في بنيتها الأصلية وتخطيط الذاكرة ويقوم برنامج التشغيل بنسخها على الفور إلى العملية المستهدفة. بعد أن تكون البيانات في العملية المستهدفة، يكون الهيكل وتخطيط الذاكرة متماثلين ويمكن قراءة البيانات دون الحاجة إلى نسخة أخرى.

قفل دقيق الحبيبات

Common-4.4 وما فوق، بما في ذلك المنبع

في إصدارات Android السابقة، استخدم برنامج تشغيل Binder قفلًا عامًا للحماية من الوصول المتزامن إلى هياكل البيانات المهمة. بينما كان هناك حد أدنى من التنافس على القفل، كانت المشكلة الرئيسية هي أنه إذا حصل خيط ذو أولوية منخفضة على القفل ثم تم استباقه، فقد يؤدي ذلك إلى تأخير كبير في سلاسل الرسائل ذات الأولوية الأعلى التي تحتاج إلى الحصول على نفس القفل. وقد تسبب هذا في حدوث خلل في المنصة.

تضمنت المحاولات الأولية لحل هذه المشكلة تعطيل الشفعة أثناء الضغط على القفل العام. ومع ذلك، كان هذا بمثابة اختراق أكثر من كونه حلًا حقيقيًا، وقد تم رفضه في النهاية من قبل المنبع وتم التخلص منه. ركزت المحاولات اللاحقة على جعل القفل أكثر دقة، حيث تم تشغيل نسخة منها على أجهزة Pixel منذ يناير 2017. وبينما تم الإعلان عن غالبية هذه التغييرات، تم إجراء تحسينات جوهرية في الإصدارات اللاحقة.

بعد تحديد المشكلات الصغيرة في تنفيذ القفل الدقيق، ابتكرنا حلاً محسنًا ببنية قفل مختلفة وأرسلنا التغييرات في جميع فروع kernel الشائعة. نواصل اختبار هذا التنفيذ على عدد كبير من الأجهزة المختلفة؛ نظرًا لأننا لسنا على علم بأي مشكلات معلقة، فهذا هو التطبيق الموصى به للأجهزة التي يتم شحنها بنظام Android 8.

الميراث الأولوية في الوقت الحقيقي

Common-4.4 وcommon-4.9 (المنبع قريبًا)

لقد دعم برنامج تشغيل الموثق دائمًا وراثة الأولوية الجيدة. نظرًا لأن عددًا متزايدًا من العمليات في Android تعمل بأولوية الوقت الفعلي، فمن المنطقي الآن في بعض الحالات أنه إذا قام مؤشر ترابط في الوقت الفعلي بإجراء مكالمة موثقة، فإن مؤشر الترابط في العملية التي تعالج تلك المكالمة يعمل أيضًا بأولوية الوقت الفعلي . لدعم حالات الاستخدام هذه، يقوم Android 8 الآن بتنفيذ وراثة الأولوية في الوقت الفعلي في برنامج تشغيل الموثق.

بالإضافة إلى وراثة الأولوية على مستوى المعاملة، يسمح وراثة أولوية العقدة للعقدة (كائن خدمة الموثق) بتحديد الحد الأدنى للأولوية التي يجب عندها تنفيذ الاستدعاءات إلى هذه العقدة. الإصدارات السابقة من Android تدعم بالفعل وراثة أولوية العقدة بقيم لطيفة، لكن Android 8 يضيف دعمًا لسياسات الجدولة في الوقت الفعلي وراثة العقدة.

تغييرات مساحة المستخدم

يتضمن Android 8 جميع تغييرات مساحة المستخدم المطلوبة للعمل مع برنامج تشغيل الموثق الحالي في kernel المشترك مع استثناء واحد: التنفيذ الأصلي لتعطيل وراثة الأولوية في الوقت الفعلي لـ /dev/binder استخدم ioctl . أدى التطوير اللاحق إلى تحويل التحكم في وراثة الأولوية إلى طريقة أكثر دقة تكون لكل وضع رابط (وليس لكل سياق). وبالتالي، فإن ioctl ليس موجودًا في فرع Android المشترك ويتم تقديمه بدلاً من ذلك في حباتنا المشتركة .

تأثير هذا التغيير هو تعطيل وراثة الأولوية في الوقت الفعلي افتراضيًا لكل عقدة. لقد وجد فريق أداء Android أنه من المفيد تمكين وراثة الأولوية في الوقت الفعلي لجميع العقد في مجال hwbinder . لتحقيق نفس التأثير، اختر هذا التغيير في مساحة المستخدم.

SHAs للنوى المشتركة

للحصول على التغييرات اللازمة لبرنامج تشغيل الموثق، قم بالمزامنة مع SHA المناسب:

  • مشترك-3.18
    cc8b90c121de ANDROID: Binder: لا تتحقق من الأذونات المسبقة عند الاستعادة.
  • مشترك-4.4
    76b376eac7a2 ANDROID: الموثق: لا تتحقق من الأذونات المسبقة عند الاستعادة.
  • مشترك-4.9
    ecd972d4f9b5 ANDROID: الموثق: لا تتحقق من الأذونات الأولية عند الاستعادة.

باستخدام الموثق IPC

تاريخيًا، استخدمت عمليات البائع الاتصال بين العمليات (IPC) للتواصل. في Android 8، تصبح عقدة الجهاز /dev/binder حصرية لعمليات إطار العمل، مما يعني أن عمليات البائع لم يعد بإمكانها الوصول إليها. يمكن لعمليات البائع الوصول إلى /dev/hwbinder ، لكن يجب عليها تحويل واجهات AIDL الخاصة بها لاستخدام HIDL. بالنسبة للموردين الذين يرغبون في الاستمرار في استخدام واجهات AIDL بين عمليات البائعين، يدعم Android Binder IPC كما هو موضح أدناه. في Android 10، يسمح Stable AIDL لجميع العمليات باستخدام /dev/binder مع حل ضمانات الاستقرار التي تم حلها أيضًا HIDL و /dev/hwbinder . للتعرف على كيفية استخدام Stable AIDL، راجع AIDL لـ HALs .

com.vndbinder

يدعم Android 8 نطاق Binder جديدًا للاستخدام بواسطة خدمات البائعين، ويمكن الوصول إليه باستخدام /dev/vndbinder بدلاً من /dev/binder . مع إضافة /dev/vndbinder ، أصبح لدى Android الآن نطاقات IPC الثلاثة التالية:

مجال IPC وصف
/dev/binder IPC بين عمليات الإطار/التطبيق مع واجهات AIDL
/dev/hwbinder IPC بين عمليات الإطار/البائع مع واجهات HIDL
IPC بين عمليات البائع مع واجهات HIDL
/dev/vndbinder IPC بين عمليات البائع/المورد مع واجهات AIDL

لكي يظهر /dev/vndbinder ، تأكد من تعيين عنصر تكوين kernel CONFIG_ANDROID_BINDER_DEVICES على "binder,hwbinder,vndbinder" (هذا هو الإعداد الافتراضي في أشجار kernel الشائعة في Android).

عادةً، لا تفتح عمليات البائع برنامج تشغيل الموثق مباشرةً، بل ترتبط بدلاً من ذلك بمكتبة مساحة المستخدم libbinder ، التي تفتح برنامج تشغيل الموثق. تؤدي إضافة طريقة لـ ::android::ProcessState() إلى تحديد برنامج تشغيل الموثق لـ libbinder . يجب أن تستدعي عمليات البائع هذه الطريقة قبل الاتصال بـ ProcessState, IPCThreadState أو قبل إجراء أي استدعاءات للموثق بشكل عام. للاستخدام، ضع الاستدعاء التالي بعد main() لعملية البائع (العميل والخادم):

ProcessState::initWithDriver("/dev/vndbinder");

vndservicemanager

في السابق، كانت خدمات Binder مسجلة في servicemanager ، حيث يمكن استعادتها من خلال عمليات أخرى. في Android 8، يتم الآن استخدام servicemanager حصريًا من خلال عمليات إطار العمل والتطبيق ولم يعد بإمكان عمليات البائعين الوصول إليه.

ومع ذلك، يمكن لخدمات البائعين الآن استخدام vndservicemanager ، وهو مثيل جديد لـ servicemanager يستخدم /dev/vndbinder بدلاً من /dev/binder والذي تم إنشاؤه من نفس المصادر مثل Framework servicemanager . لا تحتاج عمليات البائع إلى إجراء تغييرات للتحدث مع vndservicemanager ؛ عند فتح عملية البائع / dev/vndbinder ، تنتقل عمليات البحث عن الخدمة تلقائيًا إلى vndservicemanager .

يتم تضمين الملف الثنائي vndservicemanager في ملفات تكوين الأجهزة الافتراضية لنظام Android.

سياسة سيلينوكس

تحتاج عمليات المورد التي ترغب في استخدام وظيفة الموثق للتواصل مع بعضها البعض إلى ما يلي:

  1. الوصول إلى /dev/vndbinder .
  2. يتم ربط Binder {transfer, call} بـ vndservicemanager .
  3. binder_call(A, B) لأي مجال بائع A يريد الاتصال بمجال البائع B عبر واجهة رابط البائع.
  4. إذن {add, find} الخدمات في vndservicemanager .

للوفاء بالمتطلبات 1 و2، استخدم الماكرو vndbinder_use() :

vndbinder_use(some_vendor_process_domain);

للوفاء بالمتطلبات 3، يمكن أن يظل binder_call(A, B) لعمليات البائع A وB التي تحتاج إلى التحدث عبر الموثق في مكانه، ولا يحتاج إلى إعادة تسمية.

للوفاء بالمتطلبات 4، يجب عليك إجراء تغييرات في طريقة التعامل مع أسماء الخدمات وتسمياتها وقواعدها.

للحصول على تفاصيل حول SELinux، راجع Linux المحسّن للأمان في Android . للحصول على تفاصيل حول SELinux في Android 8.0، راجع SELinux لنظام Android 8.0 .

أسماء الخدمة

في السابق، كان البائع يعالج أسماء الخدمات المسجلة في ملف service_contexts ويضيف القواعد المقابلة للوصول إلى هذا الملف. مثال لملف service_contexts من device/google/marlin/sepolicy :

AtCmdFwd                              u:object_r:atfwd_service:s0
cneservice                            u:object_r:cne_service:s0
qti.ims.connectionmanagerservice      u:object_r:imscm_service:s0
rcs                                   u:object_r:radio_service:s0
uce                                   u:object_r:uce_service:s0
vendor.qcom.PeripheralManager         u:object_r:per_mgr_service:s0

في Android 8، يقوم vndservicemanager بتحميل ملف vndservice_contexts بدلاً من ذلك. يجب إضافة خدمات البائع التي يتم ترحيلها إلى vndservicemanager (والموجودة بالفعل في ملف service_contexts القديم) إلى ملف vndservice_contexts الجديد.

تسميات الخدمة

في السابق، تم تعريف تسميات الخدمة مثل u:object_r:atfwd_service:s0 في ملف service.te . مثال:

type atfwd_service,      service_manager_type;

في Android 8، يجب عليك تغيير النوع إلى vndservice_manager_type ونقل القاعدة إلى ملف vndservice.te . مثال:

type atfwd_service,      vndservice_manager_type;

قواعد مدير الخدمة

في السابق، كانت القواعد تمنح النطاقات حق الوصول لإضافة الخدمات أو البحث عنها من servicemanager . مثال:

allow atfwd atfwd_service:service_manager find;
allow some_vendor_app atfwd_service:service_manager add;

في Android 8، يمكن أن تظل هذه القواعد في مكانها وتستخدم نفس الفئة. مثال:

allow atfwd atfwd_service:service_manager find;
allow some_vendor_app atfwd_service:service_manager add;