AIDL برای HAL ها

اندروید 11 قابلیت استفاده از AIDL برای HAL ها را در اندروید معرفی می کند. این امکان پیاده سازی بخش هایی از اندروید را بدون HIDL فراهم می کند. HAL های انتقالی را به استفاده انحصاری از AIDL در صورت امکان تغییر دهید (زمانی که HAL های بالادست از HIDL استفاده می کنند، HIDL باید استفاده شود).

HAL هایی که از AIDL برای برقراری ارتباط بین اجزای چارچوب، مانند اجزای موجود در system.img و اجزای سخت افزاری، مانند آنهایی که در vendor.img استفاده می کنند، باید از AIDL پایدار استفاده کنند. با این حال، برای برقراری ارتباط در یک پارتیشن، به عنوان مثال از یک HAL به دیگری، هیچ محدودیتی در مکانیسم IPC برای استفاده وجود ندارد.

انگیزه

AIDL طولانی‌تر از HIDL بوده است و در بسیاری از مکان‌های دیگر مانند بین اجزای چارچوب Android یا در برنامه‌ها استفاده می‌شود. اکنون که AIDL از پشتیبانی پایدار برخوردار است، امکان پیاده‌سازی کل پشته با یک زمان اجرا IPC وجود دارد. AIDL همچنین سیستم نسخه سازی بهتری نسبت به HIDL دارد.

  • استفاده از یک زبان IPC به معنای داشتن تنها یک چیز برای یادگیری است، اشکال زدایی، بهینه سازی و ایمن سازی.
  • AIDL از نسخه سازی در محل برای دارندگان یک رابط پشتیبانی می کند:
    • مالکان می‌توانند روش‌هایی را به انتهای رابط‌ها یا فیلدهایی را به بسته‌بندی‌ها اضافه کنند. این بدان معناست که کد نسخه در طول سال‌ها آسان‌تر است، و همچنین هزینه سال به سال کمتر است (انواع را می‌توان در محل اصلاح کرد و نیازی به کتابخانه‌های اضافی برای هر نسخه رابط نیست).
    • رابط های برنامه افزودنی را می توان در زمان اجرا و نه در سیستم نوع متصل کرد، بنابراین نیازی به تغییر برنامه های افزودنی پایین دستی به نسخه های جدیدتر اینترفیس ها نیست.
  • یک رابط AIDL موجود می تواند مستقیماً زمانی که مالک آن تصمیم به تثبیت آن می کند استفاده شود. قبل از این، یک نسخه کامل از رابط باید در HIDL ایجاد شود.

نوشتن رابط 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 ها در همان دایرکتوری های پایه رابط های aidl ، در پوشه های idl قرار دارند.

  • سخت افزار/رابط
  • چارچوب/سخت افزار/اینترفیس
  • سیستم/سخت افزار/رابط

شما باید واسط های افزونه را در زیر شاخه hardware/interfaces دیگر در vendor یا hardware قرار دهید.

رابط های برنامه افزودنی

اندروید با هر نسخه دارای مجموعه ای از رابط های رسمی AOSP است. وقتی شرکای Android می‌خواهند قابلیت‌هایی را به این رابط‌ها اضافه کنند، نباید مستقیماً این رابط‌ها را تغییر دهند زیرا این بدان معناست که زمان اجرا اندروید آنها با زمان اجرا اندروید AOSP سازگار نیست. برای دستگاه های GMS، اجتناب از تغییر این رابط ها نیز چیزی است که تضمین می کند تصویر GSI می تواند به کار خود ادامه دهد.

برنامه های افزودنی می توانند به دو روش مختلف ثبت شوند:

با این حال یک برنامه افزودنی ثبت شده است، زمانی که اجزای خاص فروشنده (به این معنی که بخشی از AOSP بالادستی نیست) از رابط استفاده می کنند، امکان تداخل ادغام وجود ندارد. با این حال، هنگامی که اصلاحات پایین‌دستی برای اجزای AOSP بالادست انجام می‌شود، تضادهای ادغام می‌تواند منجر شود و استراتژی‌های زیر توصیه می‌شود:

  • افزودنی های رابط را می توان در نسخه بعدی به AOSP ارتقا داد
  • افزودنی‌های رابط که به انعطاف‌پذیری بیشتر اجازه می‌دهند، بدون تضاد ادغام، می‌توانند در نسخه بعدی به بالادستی شوند

Extension Parcelables: 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

AIDL دارای سه باطن مختلف است: جاوا، NDK، CPP. برای استفاده از Stable AIDL، همیشه باید از نسخه سیستمی libbinder در system/lib*/libbinder.so و در /dev/binder صحبت کنید. برای کد روی تصویر فروشنده، این بدان معنی است که libbinder (از VNDK) نمی تواند استفاده شود: این کتابخانه دارای یک C++ API ناپایدار و داخلی های ناپایدار است. در عوض، کد فروشنده بومی باید از پشتیبان NDK AIDL، پیوند در برابر libbinder_ndk (که توسط system libbinder.so پشتیبانی می‌شود) و در برابر کتابخانه‌های -ndk_platform ایجاد شده توسط ورودی‌های aidl_interface استفاده کند.

نام های نمونه سرور 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، مراحل زیر را دنبال کنید:

  1. ابزار واقع در system/tools/hidl/hidl2aidl .

    ساخت این ابزار از آخرین منبع کامل ترین تجربه را ارائه می دهد. می توانید از آخرین نسخه برای تبدیل رابط ها در شاخه های قدیمی تر از نسخه های قبلی استفاده کنید.

    m hidl2aidl
    
  2. ابزار را با دایرکتوری خروجی و به دنبال آن بسته ای که باید تبدیل شود اجرا کنید.

    hidl2aidl -o <output directory> <package>
    

    مثلا:

    hidl2aidl -o . android.hardware.nfc@1.2
    
  3. فایل های تولید شده را بخوانید و مشکلات مربوط به تبدیل را برطرف کنید.

    • conversion.log حاوی هر گونه مشکل کنترل نشده است که ابتدا باید برطرف شود.
    • فایل‌های .aidl ایجاد شده ممکن است هشدارها و پیشنهاداتی داشته باشند که ممکن است نیاز به اقدام داشته باشند. این نظرات با // شروع می شود.
    • از فرصت برای پاکسازی و بهبود بسته استفاده کنید.
  4. فقط اهداف مورد نیاز خود را بسازید.

    • پشتیبان هایی که استفاده نمی شوند را غیرفعال کنید. باطن NDK را به باطن CPP ترجیح دهید، به انتخاب زمان اجرا مراجعه کنید.
    • کتابخانه‌های ترجمه یا هر کد تولید شده‌ای که استفاده نمی‌شود را حذف کنید.
  5. تفاوت های عمده AIDL/HIDL را ببینید.

    • استفاده از Status داخلی AIDL و استثناها معمولاً اینترفیس را بهبود می بخشد و نیاز به نوع وضعیت خاص رابط دیگر را برطرف می کند.

سیاست برای HAL های AIDL

یک نوع سرویس AIDL که برای کد فروشنده قابل مشاهده است باید دارای ویژگی vendor_service باشد. در غیر این صورت، پیکربندی sepolicy مانند هر سرویس AIDL دیگری است (اگرچه ویژگی های خاصی برای HAL ها وجود دارد). در اینجا یک تعریف مثال از زمینه سرویس HAL آورده شده است:

    type hal_foo_service, service_manager_type, vendor_service;

برای اکثر سرویس‌هایی که توسط پلتفرم تعریف شده‌اند، یک زمینه سرویس با نوع صحیح از قبل اضافه شده است (به عنوان مثال، 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, vendor_service, 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_call(hal_foo_server, servicemanager)

    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
    binder_use(some_hal_server_domain)
    hal_server_domain(some_hal_server_domain, hal_foo)

رابط های افزونه پیوست شده

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

هر زمان که یک برنامه افزودنی عملکرد HAL موجود را تغییر می‌دهد، باید از افزونه‌های پیوست استفاده شود. هنگامی که به عملکرد کاملاً جدیدی نیاز است، این مکانیسم نیازی به استفاده ندارد و رابط برنامه افزودنی را می توان مستقیماً با مدیر سرویس ثبت کرد. اینترفیس های الحاقی زمانی که به واسط های فرعی متصل می شوند بیشترین معنا را دارند، زیرا این سلسله مراتب ممکن است عمیق یا چند نمونه ای باشند. استفاده از یک برنامه افزودنی جهانی برای منعکس کردن سلسله مراتب رابط کلاسور یک سرویس دیگر به حسابداری گسترده نیاز دارد تا عملکردی معادل برای برنامه های افزودنی پیوست شده مستقیماً ارائه دهد.

برای تنظیم یک افزونه در binder، از API های زیر استفاده کنید:

  • در باطن NDK: AIBinder_setExtension
  • در باطن جاوا: android.os.Binder.setExtension
  • در باطن CPP: android::Binder::setExtension

برای دریافت پسوند بر روی بایندر، از API های زیر استفاده کنید:

  • در باطن NDK: AIBinder_getExtension
  • در باطن جاوا: android.os.IBinder.getExtension
  • در باطن CPP: android::IBinder::getExtension

می‌توانید اطلاعات بیشتری برای این 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 استفاده کند.