استفاده از IPC چسباننده

این صفحه تغییرات درایور binder در Android 8 را شرح می‌دهد، جزئیاتی درباره استفاده از binder IPC ارائه می‌دهد، و خط‌مشی SELinux موردنیاز را فهرست می‌کند.

تغییرات در درایور دستگاه صحافی

از Android 8، چارچوب Android و HAL اکنون بااستفاده از binder با یکدیگر ارتباط برقرار می‌کنند. ازآنجایی‌که این ارتباط ترافیک binder را به‌طور چشمگیری افزایش می‌دهد، Android 8 شامل چندین بهبود است که برای سریع نگه داشتن IPC binder طراحی شده‌اند. فروشندگان SoC و OEM باید مستقیماً از شاخه‌های مربوطه android-4.4،‏ android-4.9، و نسخه‌های بالاتر پروژه kernel/common ادغام کنند.

دامنه‌های چنداتصالی (زمینه‌ها)

‫Common-4.4 و بالاتر، ازجمله بالادست

برای تقسیم تمیز ترافیک بایندر بین چارچوب (مستقل از دستگاه) و کد فروشنده (مخصوص دستگاه)، Android 8 مفهوم زمینه بایندر را معرفی کرد. هر بافت پیونددهنده گره دستگاه و مدیر بافت (سرویس) خودش را دارد. فقط ازطریق گره دستگاهی که مدیر بافت به آن تعلق دارد می‌توانید به مدیر بافت دسترسی داشته باشید و وقتی گره چسباننده ازطریق بافت خاصی ارسال می‌شود، فقط ازطریق همان بافت توسط فرایند دیگری قابل‌دسترسی است، بنابراین دامنه‌ها کاملاً از یکدیگر جدا می‌شوند. برای جزئیات استفاده، vndbinder و vndservicemanager را ببینید.

پراکندگی-جمع‌آوری

‫Common-4.4 و بالاتر، ازجمله بالادست

در نسخه‌های قبلی Android، هر بخش از داده‌ها در تماس پیوند سه بار کپی می‌شد:

  • یک‌بار برای تبدیل آن به Parcel در فرایند تماس
  • یک‌بار در درایور هسته برای کپی کردن Parcel در فرایند هدف
  • یک‌بار برای غیرسریال‌سازی Parcel در فرایند هدف

‫Android 8 از بهینه‌سازی پراکندگی-جمع‌آوری برای کاهش تعداد نسخه‌ها از ۳ به ۱ استفاده می‌کند. به‌جای سریال‌سازی داده‌ها ابتدا در Parcel، داده‌ها در ساختار و چیدمان حافظه اصلی خود باقی می‌مانند و درایور بلافاصله آن‌ها را در فرایند هدف کپی می‌کند. پس‌از اینکه داده‌ها در فرایند هدف قرار گرفت، ساختار و چیدمان حافظه یکسان است و داده‌ها را می‌توان بدون نیاز به کپی دیگری خواند.

قفل‌گذاری دقیق

‫Common-4.4 و بالاتر، ازجمله بالادست

در نسخه‌های قبلی Android، درایور binder از قفل سراسری برای محافظت دربرابر دسترسی هم‌زمان به ساختارهای داده حیاتی استفاده می‌کرد. اگرچه رقابت برای قفل حداقل بود، مشکل اصلی این بود که اگر رشته‌ای با اولویت پایین قفل را به‌دست می‌آورد و سپس پیش‌گیری می‌شد، می‌توانست رشته‌های با اولویت بالاتر را که نیاز به به‌دست آوردن همان قفل داشتند به‌طور جدی به‌تأخیر بیندازد. این امر باعث ایجاد لرزش در پلاتفرم شد.

تلاش‌های اولیه برای حل این مشکل شامل غیرفعال کردن پیش‌دستی هنگام نگه داشتن قفل سراسری بود. بااین‌حال، این بیشتر یک راهکار موقت بود تا یک راه‌حل واقعی، و درنهایت توسط upstream رد شد و کنار گذاشته شد. تلاش‌های بعدی بر دقیق‌تر کردن قفل کردن متمرکز بود، نسخه‌ای از آن از ژانویه ۲۰۱۷ در دستگاه‌های Pixel اجرا می‌شود. اگرچه اکثر آن تغییرات عمومی شدند، اما در نسخه‌های بعدی بهبودهای قابل‌توجهی ایجاد شد.

پس‌از شناسایی مشکلات جزئی در پیاده‌سازی قفل‌گذاری دقیق، راه‌حل بهبودیافته‌ای با معماری قفل‌گذاری متفاوت طراحی کردیم و تغییرات را در همه شاخه‌های رایج هسته ارسال کردیم. ما همچنان این پیاده‌سازی را در تعداد زیادی از دستگاه‌های مختلف آزمایش می‌کنیم؛ ازآنجایی‌که از هیچ مشکل حل‌نشده‌ای مطلع نیستیم، این پیاده‌سازی برای دستگاه‌هایی که با Android 8 عرضه می‌شوند توصیه می‌شود.

وراثت اولویت هم‌زمان

Common-4.4 و common-4.9 (بالادستی به‌زودی ارائه می‌شود)

گرداننده چسباننده همیشه از وراثت اولویت خوب پشتیبانی کرده است. As an increasing number of processes in Android run at real-time priority, in some cases it now makes sense that if a real-time thread makes a binder call, the thread in the process that handles that call also runs at real-time priority. برای پشتیبانی از این موارد استفاده، Android 8 اکنون وراثت اولویت هم‌زمان را در درایور binder پیاده‌سازی می‌کند.

علاوه‌بر وراثت اولویت در سطح تراکنش، وراثت اولویت گره به گره (شیء سرویس binder) اجازه می‌دهد حداقل اولویتی را که فراخوانی‌های این گره باید در آن اجرا شود مشخص کند. نسخه‌های قبلی Android ازقبل از اولویت گره با مقادیر nice پشتیبانی می‌کردند، اما Android 8 از وراثت گره خط‌مشی‌های زمان‌بندی بی‌درنگ پشتیبانی می‌کند.

تغییرات فضای کاربر

‫Android 8 شامل همه تغییرات فضای کاربر موردنیاز برای کار با گرداننده پیونددهنده فعلی در هسته مشترک با یک استثنا است: پیاده‌سازی اصلی برای غیرفعال کردن وراثت اولویت بلادرنگ برای /dev/binder از ioctl استفاده می‌کرد. توسعه بعدی کنترل وراثت اولویت را به روشی دقیق‌تر تغییر داد که براساس حالت binder (و نه براساس زمینه) است. بنابراین، 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.

کار با binder IPC

ازنظر تاریخی، فرایندهای فروشنده از ارتباط بین‌فرایندی (IPC) با استفاده از binder برای ارتباط استفاده کرده‌اند. در Android 8، گره دستگاه /dev/binder به‌صورت انحصاری دراختیار فرایندهای چارچوب قرار می‌گیرد، یعنی فرایندهای فروشنده دیگر به آن دسترسی ندارند. فرایندهای فروشنده می‌توانند به /dev/hwbinder دسترسی داشته باشند، اما باید از میاناهای AIDL خود برای استفاده از HIDL تبدیل کنند. برای فروشندگانی که می‌خواهند به استفاده از میاناهای AIDL بین فرایندهای فروشنده ادامه دهند، Android از IPC چسباننده همان‌طور که در زیر توضیح داده شده است پشتیبانی می‌کند. در Android 10، «زبان توصیف واسط Android پایدار» به همه فرایندها اجازه می‌دهد از /dev/binder استفاده کنند و درعین‌حال مشکلات مربوط به تضمین‌های پایداری که HIDL و /dev/hwbinder حل کرده بودند را نیز برطرف می‌کند. برای نحوه استفاده از Stable AIDL، به AIDL برای HAL مراجعه کنید.

vndbinder

‫Android 8 از دامنه binder جدیدی برای استفاده توسط سرویس‌های فروشنده پشتیبانی می‌کند که بااستفاده از /dev/vndbinder به‌جای /dev/binder قابل‌دسترسی است. با افزوده شدن /dev/vndbinder، Android اکنون سه دامنه IPC زیر را دارد:

دامنه IPC شرح
/dev/binder ‫IPC بین فرایندهای چارچوب/برنامه با میاناهای AIDL
/dev/hwbinder ارتباط بین‌فرایندی بین فرایندهای چارچوب/فروشنده با میاناهای HIDL
ارتباط بین‌فرایندی بین فرایندهای فروشنده با میاناهای HIDL
/dev/vndbinder ارتباط بین‌فرایندی بین فرایندهای فروشنده/فروشنده با «میاناهای AIDL»

برای اینکه /dev/vndbinder نشان داده شود، مطمئن شوید عنصر پیکربندی هسته CONFIG_ANDROID_BINDER_DEVICES روی "binder,hwbinder,vndbinder" تنظیم شده باشد (این تنظیم پیش‌فرض در درخت‌های هسته مشترک Android است).

به‌طور معمول، فرایندهای فروشنده مستقیماً درایور چسباننده را باز نمی‌کنند و درعوض با کتابخانه فضای کاربر libbinder پیوند برقرار می‌کنند که درایور چسباننده را باز می‌کند. افزودن روشی برای ::android::ProcessState() درایور binder را برای libbinder انتخاب می‌کند. فرایندهای فروشنده باید این روش را قبل‌از تماس با ProcessState, IPCThreadState، یا قبل‌از برقراری هرگونه تماس با پوشه به‌طورکلی، فراخوانی کنند. برای استفاده، تماس زیر را پس‌از main() فرایند فروشنده (کارخواه و سرور) قرار دهید:

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

vndservicemanager

قبلاً، خدمات پیونددهنده با servicemanager ثبت می‌شد، جایی که می‌توانست توسط فرایندهای دیگر بازیابی شود. در Android 8، اکنون servicemanager منحصراً توسط چارچوب و فرایندهای برنامه استفاده می‌شود و فرایندهای فروشنده دیگر نمی‌توانند به آن دسترسی داشته باشند.

بااین‌حال، سرویس‌های فروشنده اکنون می‌توانند از vndservicemanager، نمونه جدیدی از servicemanager که از /dev/vndbinder به‌جای /dev/binder استفاده می‌کند و از همان منابع چارچوب servicemanager ساخته شده است، استفاده کنند. فرایندهای فروشنده برای صحبت کردن با vndservicemanager نیازی به تغییر ندارند؛ وقتی فرایند فروشنده /dev/vndbinder را باز می‌کند، جستجوهای سرویس به‌طور خودکار به vndservicemanager می‌رود.

دودویی vndservicemanager در فایل‌های ساخت دستگاه پیش‌فرض Android قرار دارد.

خط‌مشی SELinux

فرایندهای فروشنده که می‌خواهند از عملکرد binder برای برقراری ارتباط با یکدیگر استفاده کنند به موارد زیر نیاز دارند:

  1. دسترسی به /dev/vndbinder.
  2. دستگاه صحافی {transfer, call} به vndservicemanager قلاب می‌شود.
  3. binder_call(A, B) برای هر دامنه فروشنده A که می‌خواهد ازطریق میانای چسباننده فروشنده با دامنه فروشنده B تماس بگیرد.
  4. اجازه {add, find} سرویس در vndservicemanager.

برای برآورده کردن الزامات ۱ و ۲، از vndbinder_use() کلان‌دستور استفاده کنید:

vndbinder_use(some_vendor_process_domain);

برای برآورده کردن الزام ۳، binder_call(A, B) برای فرایندهای A و B فروشنده که باید ازطریق binder باهم ارتباط برقرار کنند می‌تواند در جای خود باقی بماند و نیازی به تغییر نام ندارد.

برای برآورده کردن الزام ۴، باید در نحوه مدیریت نام‌های سرویس، برچسب‌های سرویس، و قوانین تغییراتی ایجاد کنید.

برای جزئیات مربوط به SELinux، به Security-Enhanced 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;