توضِّح هذه الصفحة التغييرات التي تم إجراؤها على برنامج تشغيل أداة الربط في Android 8، وتقدِّم تفاصيل عن استخدام أداة الربط لبروتوكول "التواصل بين العمليات"، وتسرد سياسة SELinux المطلوبة.
التغييرات في برنامج تشغيل الربط
بدءًا من Android 8، يتواصل إطار عمل Android وواجهات HAL معًا باستخدام أداة الربط. وبما أنّ هذا التواصل يزيد بشكل كبير من عدد عمليات معالجة ملف binder، يتضمّن Android 8 عدة تحسينات مصمّمة للحفاظ على سرعة معالجة ملف binder. على مورّدي شرائح المعالجة المتقدّمة والمصنّعين الأصليّين للأجهزة دمج الإصدارات مباشرةً من الفروع ذات الصلة من android-4.4 وandroid-4.9 والإصدارات الأحدث من project kernel/common.
نطاقات ربط متعددة (سياقات)
الإصدار الشائع 4.4 والإصدارات الأحدث، بما في ذلك الإصدارات السابقةلتقسيم عدد زيارات أداة الربط بدقة بين إطار العمل (الذي لا يعتمد على الجهاز) وسياق المورّد (الذي يعتمد على الجهاز)، قدّم نظام التشغيل Android 8 مفهوم سياق أداة الربط. يحتوي كل سياق رابط على عقدة جهاز خاصة به ومدير سياق (خدمة) خاص به. لا يمكنك الوصول إلى "مدير السياق" إلا من خلال ملف الجهاز الذي ينتمي إليه، وعند تمرير ملف ربط من خلال ملف سياق معيّن، لا يمكن الوصول إليه من ذلك السياق نفسه إلا من خلال عملية أخرى، وبالتالي يتم عزل النطاقات عن بعضها تمامًا. لمعرفة تفاصيل عن الاستخدام، يُرجى الاطّلاع على vndbinder و vndservicemanager.
الانتشار والتجمع
الإصدار الشائع 4.4 والإصدارات الأحدث، بما في ذلك الإصدارات السابقةفي الإصدارات السابقة من Android، كان يتم نسخ كل قطعة من البيانات في طلب الربط ثلاث مرات:
- مرة واحدة لتسلسلها إلى
Parcel
في عملية الاتصال - بعد الدخول إلى برنامج تشغيل kernel لنسخ
Parcel
إلى العملية المستهدفة - مرة واحدة لإلغاء تسلسل
Parcel
في العملية المستهدَفة
يستخدم نظام التشغيل Android 8 ميزة
تحسين "الجمع والتشتيت" لتقليل عدد النُسخ من 3 إلى 1. بدلاً من
تسلسل البيانات في Parcel
أولاً، تظل البيانات في ملفه
ا الأصلي وتنسيق الذاكرة، وينسخها برنامج التشغيل على الفور إلى العملية
الهدف. بعد أن تصبح البيانات في العملية المستهدَفة، تبقى البنية وطريقة عرض الذاكرة متطابقة ويمكن قراءة البيانات بدون الحاجة إلى نسخة أخرى.
قفل دقيق
الإصدار الشائع 4.4 والإصدارات الأحدث، بما في ذلك الإصدارات السابقةفي إصدارات Android السابقة، كان برنامج تشغيل أداة الربط يستخدم قفلًا عامًا لحماية الوصول المتزامن إلى هياكل البيانات المهمة. على الرغم من أنّه كان هناك قدر ضئيل من الصراع على القفل، كانت المشكلة الرئيسية هي أنّه إذا حصلت سلسلة تعليمات ذات أولوية منخفضة على القفل ثم تم استبدالها، قد يؤدي ذلك إلى تأخير سلسلات التعليمات ذات الأولوية الأعلى التي تحتاج إلى الحصول على القفل نفسه بشكل خطير. وقد أدّى ذلك إلى حدوث تقطُّع في الأداء على المنصة.
تضمنت المحاولات الأولية لحلّ هذه المشكلة إيقاف الاستباق مع الاحتفاظ بالقفل الشامل. ومع ذلك، كان هذا الحلّ أكثر من مجرد حيلة، وقد تم رفضه في النهاية من خلال المصدر الأصلي وتم تجاهله. ركّزت المحاولات اللاحقة على جعل قفل الجهاز أكثر دقة، وقد تم تشغيل إصدار منه على أجهزة Pixel منذ كانون الثاني (يناير) 2017. على الرغم من أنّه تم نشر معظم هذه التغييرات، تم إجراء تحسينات كبيرة في الإصدارات اللاحقة.
بعد تحديد مشاكل صغيرة في تنفيذ قفل التفاصيل الدقيقة، وضعنا حلًا محسّنًا باستخدام بنية قفل مختلفة وأرسلنا التغييرات في جميع فروع النواة الشائعة. نواصل اختبار عملية التنفيذ هذه على عدد كبير من الأجهزة المختلفة، وبما أنّنا لا نعلم بأي مشاكل قائمة، فإنّنا نقترح تنفيذ هذا الإجراء على الأجهزة التي تعمل بالإصدار 8 من Android.
اكتساب الأولوية في الوقت الفعلي
Common-4.4 وcommon-4.9 (سيتم توفير الإصدارات الأحدث قريبًا)كان برنامج تشغيل الربط يدعم دائمًا اكتساب الأولوية بشكل جيد. نظرًا لأنّه يتم تنفيذ عدد متزايد من العمليات في Android بأولوية الوقت الفعلي، من المنطقي في بعض الحالات أن يتم تنفيذ سلسلة المهام في العملية التي تعالج هذا الطلب أيضًا بأولوية الوقت الفعلي إذا أجرت سلسلة مهام في الوقت الفعلي طلبًا من الرابط. ولتلبية متطلبات حالات الاستخدام هذه، ينفِّذ نظام التشغيل Android 8 الآن ميزة اكتساب الأولوية في الوقت الفعلي في برنامج تشغيل أداة الربط.
بالإضافة إلى اكتساب الأولوية على مستوى المعاملات، تسمح ميزة اكتساب أولوية العقد للعقدة (كائن خدمة الربط) بتحديد الحد الأدنى للأولوية التي يجب تنفيذ المكالمات إلى هذه العقدة عندها. كانت الإصدارات السابقة من Android تتيح اكتساب الأولوية للعقد من خلال قيم جيدة، ولكن يضيف Android 8 ميزة اكتساب الأولوية للعقد من خلال سياسات تحديد المواعيد في الوقت الفعلي.
تغييرات في مساحة المستخدم
يتضمّن الإصدار 8 من Android جميع التغييرات في مساحة المستخدم المطلوبة للعمل مع برنامج تشغيل الربط الحالي في النواة المشتركة، باستثناء واحد: عملية التنفيذ الأصلية لإيقاف اكتساب الأولوية في الوقت الفعلي لـ /dev/binder
التي تستخدم ioctl. في التطوير اللاحق، تم تبديل التحكّم في اكتساب الأولوية
إلى طريقة أكثر دقة حسب وضع الربط (وليس حسب
السياق). وبالتالي، لا يتوفّر ioctl في الفرع العام لنظام التشغيل Android، ويتم بدلاً من ذلك
إرساله في نواة النظام العام.
نتيجةً لهذا التغيير، يتم تلقائيًا إيقاف اكتساب الأولوية في الوقت الفعلي في كل عقدة. تبيّن لفريق الأداء في Android أنّه
من المفيد تفعيل اكتساب الأولوية في الوقت الفعلي لجميع العقد في
نطاق hwbinder
. لتحقيق التأثير نفسه، اختَر بعناية
هذا التغيير في مساحة المستخدم.
رموز SHA لأنظمة التشغيل الأساسية الشائعة
للحصول على التغييرات اللازمة في برنامج تشغيل الربط، عليك المزامنة مع SHA المناسب:
- Common-3.18
cc8b90c121de ANDROID: binder: don't check prio permissions on restore. - Common-4.4
76b376eac7a2 ANDROID: binder: don't check prio permissions on restore. - Common-4.9
ecd972d4f9b5 ANDROID: binder: don't check prio permissions on restore.
العمل مع واجهة برمجة التطبيقات لربط البيانات
في السابق، كانت عمليات المورّدين تستخدم تقنية "الربط" للتواصل بين العمليات
(IPC). في Android 8، تصبح /dev/binder
عقدة الجهاز
حصرية لعمليات إطار العمل، ما يعني أنّ عمليات المورّدين لم تعُد
تملك إذن الوصول إليها. يمكن لعمليات المورّدين الوصول إلى /dev/hwbinder
، ولكن
يجب تحويل واجهات AIDL لاستخدام HIDL. بالنسبة إلى المورّدين الذين يريدون مواصلة استخدام واجهات AIDL بين عمليات المورّدين، يتيح Android استخدام واجهة IPC للربط كما هو موضّح أدناه. في Android 10، يسمح ملف AIDL الثابت لجميع
العمليات باستخدام /dev/binder
مع حلّ ضمانات ثبات
HIDL و/dev/hwbinder
. لمعرفة كيفية استخدام مكتبة IDE المكتملة
AIDL، راجِع مكتبة IDE المكتملة AIDL لواجهة HAL.
vndbinder
يتيح نظام التشغيل Android 8 نطاق ربط جديدًا لاستخدامه من قِبل خدمات المورّدين، ويتم الوصول إليه باستخدام /dev/vndbinder
بدلاً من /dev/binder
. مع
إضافة /dev/vndbinder
، أصبح لدى Android الآن نطاقات
IPC الثلاثة التالية:
نطاق IPC | الوصف |
---|---|
/dev/binder |
تبادل البيانات بين عمليات التطبيق أو إطار العمل باستخدام واجهات AIDL |
/dev/hwbinder |
معالجة البيانات بين العمليات في إطار العمل أو المورّد باستخدام واجهات HIDL
معالجة البيانات بين عمليات المورّد باستخدام واجهات HIDL |
/dev/vndbinder |
واجهة برمجة التطبيقات بين عمليات المورّد/المورّد باستخدام واجهات AIDL |
لكي يظهر الخيار /dev/vndbinder
، تأكَّد من ضبط CONFIG_ANDROID_BINDER_DEVICES
في ملف تكوين kernel
على"binder,hwbinder,vndbinder"
(هذا هو الإعداد التلقائي في ملف تكوين kernel
الشائع في Android).
في العادة، لا تفتح عمليات المورّدين برنامج تشغيل الربط مباشرةً، بل يتم بدلاً من ذلك
الربط بمكتبة مساحة المستخدم libbinder
التي تفتح
برنامج تشغيل الربط. يؤدي إضافة طريقة إلى ::android::ProcessState()
إلى اختيار برنامج تشغيل الربط لـ libbinder
. يجب أن تُستخدَم هذه الطريقة قبل استدعاء ProcessState,
IPCThreadState
أو قبل إجراء أي استدعاءات لربط البيانات بشكل عام. للاستفادة من هذه الميزة، يمكنك إجراء المكالمة التالية بعد main()
لعملية المورّد
(العميل والخادم):
ProcessState::initWithDriver("/dev/vndbinder");
vndservicemanager
في السابق، كانت خدمات الربط مسجَّلة في servicemanager
،
حيث يمكن استرجاعها من خلال عمليات أخرى. في الإصدار 8 من Android، يتم استخدامservicemanager
الآن حصريًا من خلال عمليات الإطار وعمليات التطبيقات، ولم تعُد عمليات المورّدين قادرة على الوصول إليه.
ومع ذلك، يمكن الآن لخدمات المورّدين استخدام vndservicemanager
، وهو مثيل جديد
لإطار عمل servicemanager
يستخدم /dev/vndbinder
بدلاً من /dev/binder
ويتم إنشاؤه من المصادر نفسها التي يستخدمها
إطار العمل servicemanager
. لا تحتاج عمليات المورّدين إلى إجراء
تغييرات للتواصل مع vndservicemanager
. عند فتح عملية مورّد
/dev/vndbinder
، يتم تلقائيًا توجيه عمليات البحث عن الخدمات إلى
vndservicemanager
.
يتم تضمين ملف vndservicemanager
الثنائي في ملفات makefiles التلقائية لأجهزة Android.
سياسة SELinux
إنّ عمليات المورّدين التي تريد استخدام وظيفة "المُجمِّع" للتواصل مع بعضها البعض تحتاج إلى ما يلي:
- الوصول إلى
/dev/vndbinder
- يتم تثبيت المجلد
{transfer, call}
فيvndservicemanager
. binder_call(A, B)
لأي نطاق مورّد أ يريد الاتصال بالنطاق ب عبر واجهة ربط المورّد.- إذن الوصول إلى خدمات
{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
في السابق، كانت القواعد تمنح النطاقات إذن الوصول لإضافة خدمات أو العثور عليها من
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;