اندروید 11 قابلیت استفاده از AIDL برای HAL ها را در اندروید معرفی می کند. این امکان پیاده سازی بخش هایی از اندروید را بدون HIDL فراهم می کند. HAL های انتقالی را به استفاده انحصاری از AIDL در صورت امکان تغییر دهید (زمانی که HAL های بالادست از HIDL استفاده می کنند، HIDL باید استفاده شود).
HAL هایی که از AIDL برای برقراری ارتباط بین اجزای چارچوب، مانند اجزای موجود در system.img
و قطعات سخت افزاری، مانند آنهایی که در vendor.img
استفاده می کنند، باید از Stable AIDL استفاده کنند. با این حال، برای برقراری ارتباط در یک پارتیشن، به عنوان مثال، از یک HAL به دیگری، هیچ محدودیتی در مکانیسم IPC برای استفاده وجود ندارد.
انگیزه
AIDL طولانیتر از HIDL بوده است و در بسیاری از مکانهای دیگر مانند بین اجزای چارچوب Android یا در برنامهها استفاده میشود. اکنون که AIDL از پشتیبانی پایداری برخوردار است، امکان پیادهسازی کل پشته با یک زمان اجرا IPC وجود دارد. AIDL همچنین سیستم نسخه سازی بهتری نسبت به HIDL دارد.
- استفاده از یک زبان IPC به معنای داشتن تنها یک چیز برای یادگیری است، اشکال زدایی، بهینه سازی و ایمن سازی.
- AIDL از نسخه سازی در محل برای صاحبان یک رابط پشتیبانی می کند:
- مالکان میتوانند روشهایی را به انتهای رابطها یا فیلدهایی را به بستهبندیها اضافه کنند. این بدان معناست که کد نسخه در طول سالها آسانتر است، و همچنین هزینه سال به سال کمتر است (انواع را میتوان در محل اصلاح کرد و نیازی به کتابخانههای اضافی برای هر نسخه رابط نیست).
- رابط های برنامه افزودنی را می توان در زمان اجرا به جای سیستم نوع متصل کرد، بنابراین نیازی به تغییر برنامه های افزودنی پایین دستی به نسخه های جدیدتر اینترفیس ها نیست.
- یک رابط AIDL موجود میتواند مستقیماً زمانی استفاده شود که صاحب آن بخواهد آن را تثبیت کند. قبل از این، یک نسخه کامل از رابط باید در HIDL ایجاد شود.
ساخت در برابر زمان اجرا AIDL
AIDL دارای سه باطن مختلف است: جاوا، NDK، CPP. برای استفاده از Stable AIDL، همیشه باید از نسخه سیستمی libbinder در system/lib*/libbinder.so
استفاده کنید و در /dev/binder
صحبت کنید. برای کد روی تصویر فروشنده، این بدان معنی است که libbinder
(از VNDK) نمی تواند استفاده شود: این کتابخانه دارای یک C++ API ناپایدار و داخلی های ناپایدار است. در عوض، کد فروشنده بومی باید از پشتیبان NDK AIDL، پیوند در برابر libbinder_ndk
(که توسط system libbinder.so
پشتیبانی میشود) و در برابر کتابخانههای NDK ایجاد شده توسط ورودیهای aidl_interface
استفاده کند. برای نامهای دقیق ماژول، قوانین نامگذاری ماژول را ببینید.
یک رابط AIDL HAL بنویسید
برای استفاده از رابط AIDL بین سیستم و فروشنده، این رابط به دو تغییر نیاز دارد:
- هر نوع تعریف باید با
@VintfStability
حاشیه نویسی شود. - اعلان
aidl_interface
باید شاملstability: "vintf",
.
فقط صاحب یک رابط می تواند این تغییرات را انجام دهد.
وقتی این تغییرات را انجام می دهید، رابط باید در مانیفست VINTF باشد تا کار کند. با استفاده از آزمون VTS vts_treble_vintf_vendor_test
این (و الزامات مربوطه، مانند تأیید اینکه رابط های منتشر شده ثابت هستند) را آزمایش کنید. میتوانید با فراخوانی AIBinder_forceDowngradeToLocalStability
در باطن NDK، android::Stability::forceDowngradeToLocalStability
در بکاند C++، یا android.os.Binder#forceDowngradeToSystemStability
قبل از ارسال شیء پشتیبان آن در سیستم، از یک واسط @VintfStability
بدون این الزامات استفاده کنید. به فرآیند دیگری تنزل دادن یک سرویس به ثبات فروشنده در جاوا پشتیبانی نمیشود، زیرا همه برنامهها در زمینه سیستم اجرا میشوند.
علاوه بر این، برای حداکثر قابلیت حمل کد و برای جلوگیری از مشکلات احتمالی مانند کتابخانه های اضافی غیر ضروری، پشتیبان CPP را غیرفعال کنید.
توجه داشته باشید که استفاده از backends
در مثال کد زیر صحیح است، زیرا سه Backend وجود دارد (جاوا، NDK و CPP). کد زیر به شما می گوید که چگونه می توان باطن CPP را به طور خاص انتخاب کرد تا آن را غیرفعال کند.
aidl_interface: {
...
backends: {
cpp: {
enabled: false,
},
},
}
رابط های AIDL HAL را پیدا کنید
رابط های AOSP Stable AIDL برای HAL ها در همان دایرکتوری های پایه رابط های HIDL در پوشه های aidl
قرار دارند.
- سخت افزار/رابط: برای رابط هایی که معمولاً توسط سخت افزار ارائه می شود
- چارچوب/سخت افزار/اینترفیس: برای رابط های سطح بالا ارائه شده به سخت افزار
- سیستم/سخت افزار/اینترفیس: برای رابط های سطح پایین ارائه شده به سخت افزار
شما باید رابط های افزونه را در زیر شاخه های hardware/interfaces
دیگر در vendor
یا hardware
قرار دهید.
رابط های برنامه افزودنی
اندروید با هر نسخه دارای مجموعه ای از رابط های رسمی AOSP است. وقتی شرکای اندرویدی میخواهند قابلیتهایی را به این رابطها اضافه کنند، نباید مستقیماً این رابطها را تغییر دهند زیرا این بدان معناست که زمان اجرا اندروید آنها با زمان اجرا اندروید AOSP ناسازگار است. برای دستگاه های GMS، اجتناب از تغییر این رابط ها نیز چیزی است که تضمین می کند تصویر GSI می تواند به کار خود ادامه دهد.
برنامه های افزودنی می توانند به دو روش مختلف ثبت شوند:
- در زمان اجرا، پسوندهای پیوست را ببینید.
- مستقل، ثبت جهانی و در VINTF.
با این حال یک برنامه افزودنی ثبت شده است، زمانی که اجزای خاص فروشنده (به این معنی که بخشی از AOSP بالادستی نیست) از رابط استفاده می کنند، امکان تداخل ادغام وجود ندارد. با این حال، هنگامی که اصلاحات پایین دستی برای اجزای AOSP بالادست انجام می شود، تداخل ادغام می تواند منجر شود و استراتژی های زیر توصیه می شود:
- افزودنی های رابط را می توان در نسخه بعدی به AOSP ارتقا داد
- افزودنیهای رابط که به انعطافپذیری بیشتر اجازه میدهند، بدون تداخل ادغام، میتوانند در نسخه بعدی به بالادستی شوند
بسته های الحاقی: ParcelableHolder
ParcelableHolder
یک Parcelable
است که می تواند حاوی Parcelable
دیگری باشد. مورد اصلی استفاده از ParcelableHolder
این است که یک Parcelable
قابل توسعه باشد. به عنوان مثال، تصویری که پیادهکنندههای دستگاه انتظار دارند بتوانند یک Parcelable
تعریفشده توسط AOSP، AospDefinedParcelable
را گسترش دهند تا ویژگیهای ارزش افزوده آنها را در بر گیرد.
قبلاً بدون ParcelableHolder
، پیادهکنندههای دستگاه نمیتوانستند یک رابط AIDL پایدار تعریفشده توسط AOSP را تغییر دهند، زیرا اضافه کردن فیلدهای بیشتر با خطا همراه خواهد بود:
parcelable AospDefinedParcelable {
int a;
String b;
String x; // ERROR: added by a device implementer
int[] y; // added by a device implementer
}
همانطور که در کد قبلی مشاهده شد، این عمل شکسته شده است زیرا ممکن است فیلدهای اضافه شده توسط پیادهکننده دستگاه در هنگام بازبینی Parcelable در نسخههای بعدی اندروید با هم تداخل داشته باشند.
با استفاده از ParcelableHolder
، صاحب یک parcelable می تواند یک نقطه توسعه را در Parcelable
تعریف کند.
parcelable AospDefinedParcelable {
int a;
String b;
ParcelableHolder extension;
}
سپس پیادهکنندههای دستگاه میتوانند Parcelable
خود را برای پسوند خود تعریف کنند.
parcelable OemDefinedParcelable {
String x;
int[] y;
}
در نهایت، Parcelable
جدید را می توان با قسمت ParcelableHolder
به Parcelable
اصلی متصل کرد.
// Java
AospDefinedParcelable ap = ...;
OemDefinedParcelable op = new OemDefinedParcelable();
op.x = ...;
op.y = ...;
ap.extension.setParcelable(op);
...
OemDefinedParcelable op = ap.extension.getParcelable(OemDefinedParcelable.class);
// C++
AospDefinedParcelable ap;
OemDefinedParcelable op;
std::shared_ptr<OemDefinedParcelable> op_ptr = make_shared<OemDefinedParcelable>();
ap.extension.setParcelable(op);
ap.extension.setParcelable(op_ptr);
...
std::shared_ptr<OemDefinedParcelable> op_ptr;
ap.extension.getParcelable(&op_ptr);
// NDK
AospDefinedParcelable ap;
OemDefinedParcelable op;
ap.extension.setParcelable(op);
...
std::optional<OemDefinedParcelable> op;
ap.extension.getParcelable(&op);
// Rust
let mut ap = AospDefinedParcelable { .. };
let op = Rc::new(OemDefinedParcelable { .. });
ap.extension.set_parcelable(Rc::clone(&op));
...
let op = ap.extension.get_parcelable::<OemDefinedParcelable>();
نام های نمونه سرور AIDL HAL
طبق قرارداد، خدمات AIDL HAL یک نام نمونه با فرمت $package.$type/$instance
دارند. به عنوان مثال، یک نمونه از لرزاننده HAL به عنوان android.hardware.vibrator.IVibrator/default
ثبت شده است.
یک سرور AIDL HAL بنویسید
سرورهای AIDL @VintfStability
باید در مانیفست VINTF اعلان شوند، به عنوان مثال:
<hal format="aidl">
<name>android.hardware.vibrator</name>
<version>1</version>
<fqname>IVibrator/default</fqname>
</hal>
در غیر این صورت باید خدمات AIDL را به طور معمول ثبت کنند. هنگام اجرای تست های VTS، انتظار می رود که تمام AIDL HAL های اعلام شده در دسترس باشند.
یک مشتری AIDL بنویسید
مشتریان AIDL باید خود را در ماتریس سازگاری، به عنوان مثال، به شرح زیر اعلام کنند:
<hal format="aidl" optional="true">
<name>android.hardware.vibrator</name>
<version>1-2</version>
<interface>
<name>IVibrator</name>
<instance>default</instance>
</interface>
</hal>
HAL موجود را از HIDL به AIDL تبدیل کنید
از ابزار hidl2aidl
برای تبدیل رابط HIDL به AIDL استفاده کنید.
ویژگی های hidl2aidl
:
- فایل های
.aidl
را بر اساس فایل های.hal
برای بسته داده شده ایجاد کنید - قوانین ساخت بسته AIDL تازه ایجاد شده را با فعال بودن تمام backendها ایجاد کنید
- ایجاد متدهای ترجمه در پشتیبان جاوا، CPP و NDK برای ترجمه از انواع HIDL به انواع AIDL
- قوانین ساخت را برای کتابخانه های ترجمه با وابستگی های مورد نیاز ایجاد کنید
- برای اطمینان از اینکه شمارشگرهای HIDL و AIDL دارای مقادیر یکسانی در backendهای CPP و NDK هستند، اظهارات ثابت ایجاد کنید.
برای تبدیل بسته ای از فایل های hal به فایل های .aidl مراحل زیر را دنبال کنید:
ابزار واقع در
system/tools/hidl/hidl2aidl
را بسازید.ساخت این ابزار از آخرین منبع کامل ترین تجربه را ارائه می دهد. می توانید از آخرین نسخه برای تبدیل رابط ها در شاخه های قدیمی تر از نسخه های قبلی استفاده کنید.
m hidl2aidl
ابزار را با دایرکتوری خروجی و به دنبال آن بسته ای که قرار است تبدیل شود اجرا کنید.
به صورت اختیاری، از آرگومان
-l
برای افزودن محتویات یک فایل مجوز جدید به بالای همه فایل های تولید شده استفاده کنید. حتما از مجوز و تاریخ صحیح استفاده کنید.hidl2aidl -o <output directory> -l <file with license> <package>
به عنوان مثال:
hidl2aidl -o . -l my_license.txt android.hardware.nfc@1.2
فایل های تولید شده را بخوانید و مشکلات مربوط به تبدیل را برطرف کنید.
-
conversion.log
حاوی هر گونه مشکل کنترل نشده است که ابتدا باید برطرف شود. - فایلهای
.aidl
ایجاد شده ممکن است هشدارها و پیشنهاداتی داشته باشند که ممکن است نیاز به اقدام داشته باشند. این نظرات با//
شروع می شود. - از فرصت برای پاکسازی و بهبود بسته استفاده کنید.
- حاشیهنویسی
@JavaDerive
را برای ویژگیهایی که ممکن است مورد نیاز باشد، مانندtoString
یاequals
بررسی کنید.
-
فقط اهداف مورد نیاز خود را بسازید.
- پشتیبان هایی که استفاده نمی شوند را غیرفعال کنید. باطن NDK را بر باطن CPP ترجیح دهید، به انتخاب زمان اجرا مراجعه کنید.
- کتابخانههای ترجمه یا هر کد تولید شدهای که استفاده نمیشود را حذف کنید.
تفاوت های عمده AIDL/HIDL را ببینید.
- استفاده از
Status
داخلی AIDL و استثناها معمولاً رابط را بهبود می بخشد و نیاز به نوع وضعیت خاص رابط دیگر را برطرف می کند. - آرگومانهای واسط AIDL در متدها بهطور پیشفرض مانند HIDL
@nullable
نیستند.
- استفاده از
SEPolicy برای AIDL HAL
یک نوع سرویس AIDL که برای کد فروشنده قابل مشاهده است باید دارای ویژگی hal_service_type
باشد. در غیر این صورت، پیکربندی sepolicy مانند هر سرویس AIDL دیگری است (اگرچه ویژگی های خاصی برای HAL ها وجود دارد). در اینجا یک تعریف مثال از زمینه خدمات HAL آورده شده است:
type hal_foo_service, service_manager_type, hal_service_type;
برای اکثر سرویسهایی که توسط پلتفرم تعریف شدهاند، یک زمینه سرویس با نوع صحیح از قبل اضافه شده است (به عنوان مثال، android.hardware.foo.IFoo/default
قبلاً به عنوان hal_foo_service
علامتگذاری شده است). با این حال، اگر یک کلاینت فریمورک از چندین نام نمونه پشتیبانی می کند، نام نمونه های اضافی باید در فایل های service_contexts
مخصوص دستگاه اضافه شود.
android.hardware.foo.IFoo/custom_instance u:object_r:hal_foo_service:s0
وقتی نوع جدیدی از HAL ایجاد می کنیم باید ویژگی های HAL اضافه شوند. یک ویژگی HAL خاص ممکن است با چندین نوع سرویس مرتبط باشد (که هرکدام ممکن است چندین نمونه داشته باشند همانطور که قبلاً بحث کردیم). برای یک HAL، foo
، ما hal_attribute(foo)
داریم. این ماکرو ویژگی های hal_foo_client
و hal_foo_server
را تعریف می کند. برای یک دامنه معین، ماکروهای hal_client_domain
و hal_server_domain
یک دامنه را با ویژگی HAL معین مرتبط میکنند. به عنوان مثال، سرور سیستم که مشتری این HAL است با خط مشی hal_client_domain(system_server, hal_foo)
مطابقت دارد. یک سرور HAL به طور مشابه شامل hal_server_domain(my_hal_domain, hal_foo)
است. به طور معمول، برای یک ویژگی HAL داده شده، ما همچنین یک دامنه مانند hal_foo_default
برای مرجع یا نمونه HAL ایجاد می کنیم. با این حال، برخی از دستگاه ها از این دامنه ها برای سرورهای خود استفاده می کنند. تمایز بین دامنهها برای چندین سرور تنها در صورتی مهم است که چندین سرور داشته باشیم که یک رابط را ارائه میکنند و به مجموعه مجوزهای متفاوتی در پیادهسازیشان نیاز دارند. در همه این ماکروها، hal_foo
در واقع یک شیء sepolicy نیست. در عوض، این توکن توسط این ماکروها برای اشاره به گروهی از ویژگی های مرتبط با یک جفت سرور مشتری استفاده می شود.
با این حال، تا کنون، ما hal_foo_service
و hal_foo
(جفت ویژگی از hal_attribute(foo)
) را مرتبط نکرده ایم. یک ویژگی HAL با خدمات AIDL HAL با استفاده از ماکرو hal_attribute_service
مرتبط است (HIDL HAL ها از ماکرو hal_attribute_hwservice
استفاده می کنند). به عنوان مثال، hal_attribute_service(hal_foo, hal_foo_service)
. این بدان معنی است که فرآیندهای hal_foo_client
می توانند HAL را به دست آورند و فرآیندهای hal_foo_server
می توانند HAL را ثبت کنند. اجرای این قوانین ثبت نام توسط مدیر زمینه ( servicemanager
) انجام می شود. توجه داشته باشید، نام سرویسها ممکن است همیشه با ویژگیهای HAL مطابقت نداشته باشند. برای مثال، ممکن است hal_attribute_service(hal_foo, hal_foo2_service)
را ببینیم. با این حال، به طور کلی، از آنجایی که این نشان میدهد که سرویسها همیشه با هم استفاده میشوند، میتوانیم hal_foo2_service
را حذف کنیم و hal_foo_service
برای همه زمینههای سرویس خود استفاده کنیم. اکثر HAL هایی که چندین hal_attribute_service
تنظیم می کنند به این دلیل است که نام اصلی ویژگی HAL به اندازه کافی عمومی نیست و نمی توان آن را تغییر داد.
با کنار هم قرار دادن همه اینها، یک نمونه HAL به این شکل است:
public/attributes:
// define hal_foo, hal_foo_client, hal_foo_server
hal_attribute(foo)
public/service.te
// define hal_foo_service
type hal_foo_service, hal_service_type, protected_service, service_manager_type
public/hal_foo.te:
// allow binder connection from client to server
binder_call(hal_foo_client, hal_foo_server)
// allow client to find the service, allow server to register the service
hal_attribute_service(hal_foo, hal_foo_service)
// allow binder communication from server to service_manager
binder_use(hal_foo_server)
private/service_contexts:
// bind an AIDL service name to the selinux type
android.hardware.foo.IFooXxxx/default u:object_r:hal_foo_service:s0
private/<some_domain>.te:
// let this domain use the hal service
binder_use(some_domain)
hal_client_domain(some_domain, hal_foo)
vendor/<some_hal_server_domain>.te
// let this domain serve the hal service
hal_server_domain(some_hal_server_domain, hal_foo)
رابط های افزودنی پیوست شده
یک برنامه افزودنی را می توان به هر رابط بایندر متصل کرد، خواه یک رابط سطح بالا باشد که مستقیماً در سرویس مدیر ثبت شده است یا یک رابط فرعی. هنگام دریافت برنامه افزودنی، باید تأیید کنید که نوع پسوند مطابق انتظار است. برنامههای افزودنی را فقط میتوان از طریق فرآیند خدمترسانی به یک کلاسور تنظیم کرد.
هر زمان که یک برنامه افزودنی عملکرد HAL موجود را تغییر میدهد، باید از افزونههای پیوست استفاده شود. هنگامی که عملکرد کاملاً جدیدی مورد نیاز است، این مکانیسم نیازی به استفاده ندارد و یک رابط برنامه افزودنی می تواند مستقیماً با مدیر سرویس ثبت شود. اینترفیس های افزودنی پیوست شده زمانی که به واسط های فرعی متصل می شوند بیشترین معنا را دارند، زیرا این سلسله مراتب ممکن است عمیق یا چند نمونه ای باشند. استفاده از یک برنامه افزودنی جهانی برای منعکس کردن سلسله مراتب رابط کلاسور یک سرویس دیگر به حسابداری گسترده نیاز دارد تا عملکردی معادل برای برنامه های افزودنی پیوست شده مستقیم ارائه کند.
برای تنظیم یک افزونه در binder، از API های زیر استفاده کنید:
- در باطن NDK:
AIBinder_setExtension
- در باطن جاوا:
android.os.Binder.setExtension
- در باطن CPP:
android::Binder::setExtension
- در باطن Rust:
binder::Binder::set_extension
برای دریافت پسوند بر روی بایندر، از API های زیر استفاده کنید:
- در باطن NDK:
AIBinder_getExtension
- در باطن جاوا:
android.os.IBinder.getExtension
- در باطن CPP:
android::IBinder::getExtension
- در باطن Rust:
binder::Binder::get_extension
میتوانید اطلاعات بیشتری برای این APIها در مستندات تابع getExtension
در باطن مربوطه پیدا کنید. نمونه ای از نحوه استفاده از برنامه های افزودنی را می توان در سخت افزار/رابط/تست/برنامه افزودنی/ویبراتور یافت.
تفاوت های عمده AIDL و HIDL
هنگام استفاده از AIDL HAL یا استفاده از رابط های AIDL HAL، از تفاوت ها در مقایسه با نوشتن HIDL HAL آگاه باشید.
- نحو زبان AIDL به جاوا نزدیکتر است. نحو HIDL شبیه به C++ است.
- همه رابط های AIDL دارای وضعیت خطای داخلی هستند. به جای ایجاد انواع وضعیت سفارشی، ints وضعیت ثابت را در فایل های رابط ایجاد کنید و
EX_SERVICE_SPECIFIC
در پشتیبان های CPP/NDK وServiceSpecificException
در باطن جاوا استفاده کنید. به مدیریت خطا مراجعه کنید. - AIDL به طور خودکار threadpool ها را هنگام ارسال اشیاء binder شروع نمی کند. آنها باید به صورت دستی شروع شوند (به مدیریت رشته مراجعه کنید).
- AIDL در خطاهای حمل و نقل بررسی نشده سقط نمی شود (HIDL
Return
در خطاهای بررسی نشده سقط می شود). - AIDL فقط می تواند یک نوع را در هر فایل اعلام کند.
- آرگومان های AIDL را می توان علاوه بر پارامتر خروجی به صورت in/out/inout نیز مشخص کرد (هیچ "بازخوانی همزمان" وجود ندارد).
- AIDL از fd به عنوان نوع اولیه به جای دسته استفاده می کند.
- HIDL از نسخه های اصلی برای تغییرات ناسازگار و از نسخه های جزئی برای تغییرات سازگار استفاده می کند. در AIDL، تغییرات سازگار با عقب در جای خود انجام می شود. AIDL هیچ مفهوم صریحی از نسخه های اصلی ندارد. در عوض، این در نام بسته گنجانده شده است. به عنوان مثال، AIDL ممکن است از نام بسته
bluetooth2
استفاده کند. - AIDL به طور پیش فرض اولویت بلادرنگ را به ارث نمی برد. تابع
setInheritRt
باید در هر بایندر برای فعال کردن وراثت اولویت بلادرنگ استفاده شود.