خدمات & انتقال اطلاعات

این بخش نحوه ثبت و کشف سرویس ها و نحوه ارسال داده ها به یک سرویس را با فراخوانی روش های تعریف شده در رابط های موجود در فایل های .hal توضیح می دهد.

ثبت خدمات

سرورهای رابط HIDL (اشیایی که رابط را پیاده‌سازی می‌کنند) می‌توانند به عنوان سرویس‌های نام‌گذاری شده ثبت شوند. لازم نیست نام ثبت شده به رابط یا نام بسته مرتبط باشد. اگر نامی مشخص نشده باشد، از نام "پیش فرض" استفاده می شود. این باید برای HAL هایی استفاده شود که نیازی به ثبت دو پیاده سازی از یک رابط ندارند. به عنوان مثال، فراخوانی C++ برای ثبت سرویس تعریف شده در هر رابط به صورت زیر است:

status_t status = myFoo->registerAsService();
status_t anotherStatus = anotherFoo->registerAsService("another_foo_service");  // if needed

نسخه یک رابط HIDL در خود رابط گنجانده شده است. به طور خودکار با ثبت سرویس مرتبط است و می تواند از طریق فراخوانی روش ( android::hardware::IInterface::getInterfaceVersion() ) در هر رابط HIDL بازیابی شود. اشیاء سرور نیازی به ثبت نام ندارند و می‌توانند از طریق پارامترهای روش HIDL به فرآیند دیگری ارسال شوند که روش HIDL را به سرور فراخوانی می‌کند.

کشف خدمات

درخواست ها توسط کد مشتری برای یک رابط داده شده با نام و نسخه، با فراخوانی getService در کلاس HAL مورد نظر انجام می شود:

// C++
sp<V1_1::IFooService> service = V1_1::IFooService::getService();
sp<V1_1::IFooService> alternateService = V1_1::IFooService::getService("another_foo_service");
// Java
V1_1.IFooService service = V1_1.IFooService.getService(true /* retry */);
V1_1.IFooService alternateService = V1_1.IFooService.getService("another", true /* retry */);

هر نسخه از رابط HIDL به عنوان یک رابط جداگانه در نظر گرفته می شود. بنابراین، IFooService نسخه 1.1 و IFooService نسخه 2.2 هر دو می توانند به عنوان "foo_service" ثبت شوند و getService("foo_service") در هر یک از اینترفیس ها سرویس ثبت شده را برای آن رابط دریافت می کند. به همین دلیل است که در بیشتر موارد، هیچ پارامتر نامی برای ثبت یا کشف نیازی نیست (به معنی نام "پیش فرض").

شیء رابط فروشنده نیز در روش انتقال رابط برگشتی نقش دارد. برای یک رابط IFoo در بسته android.hardware.foo@1.0 ، رابطی که توسط IFoo::getService برگردانده شده است، در صورت وجود ورودی، همیشه از روش انتقال اعلام شده برای android.hardware.foo در مانیفست دستگاه استفاده می کند. و اگر روش انتقال در دسترس نباشد nullptr برگردانده می شود.

در برخی موارد، حتی بدون دریافت سرویس، ممکن است لازم باشد فورا ادامه دهید. این می تواند اتفاق بیفتد (به عنوان مثال) زمانی که یک مشتری بخواهد خود اعلان های سرویس یا در یک برنامه تشخیصی (مانند atrace ) را مدیریت کند که نیاز به دریافت همه سرویس های hw و بازیابی آنها دارد. در این مورد، API های اضافی مانند tryGetService در C++ یا getService("instance-name", false) در جاوا ارائه می شوند. API قدیمی getService ارائه شده در جاوا نیز باید همراه با اعلان‌های سرویس استفاده شود. استفاده از این API از شرایط مسابقه جلوگیری نمی کند که در آن سرور پس از درخواست مشتری با یکی از این API های بدون امتحان، خود را ثبت می کند.

اطلاعیه های مرگ سرویس

مشتریانی که می‌خواهند هنگام مرگ یک سرویس مطلع شوند، می‌توانند اعلان‌های مرگ را که توسط این چارچوب ارائه می‌شود دریافت کنند. برای دریافت اعلان ها، مشتری باید:

  1. کلاس/رابط HIDL hidl_death_recipient فرعی (در کد C++، نه در HIDL) قرار دهید.
  2. متد serviceDied() آن را لغو کنید.
  3. یک شی از زیر کلاس hidl_death_recipient را نمونه سازی کنید.
  4. متد linkToDeath() در سرویس را برای نظارت فراخوانی کنید و در شیء رابط IDeathRecipient ارسال کنید. توجه داشته باشید که این روش مالکیت گیرنده مرگ یا پروکسی را که بر اساس آن فراخوانی شده است را در اختیار نمی گیرد.

یک مثال شبه کد (C++ و Java مشابه هستند):

class IMyDeathReceiver : hidl_death_recipient {
  virtual void serviceDied(uint64_t cookie,
                           wp<IBase>& service) override {
    log("RIP service %d!", cookie);  // Cookie should be 42
  }
};
....
IMyDeathReceiver deathReceiver = new IMyDeathReceiver();
m_importantService->linkToDeath(deathReceiver, 42);

یک گیرنده مرگ ممکن است در چندین سرویس مختلف ثبت شود.

انتقال اطلاعات

داده ها ممکن است با فراخوانی روش های تعریف شده در رابط های موجود در فایل های .hal به یک سرویس ارسال شوند. دو نوع روش وجود دارد:

  • روش های مسدود کردن منتظر می مانند تا سرور نتیجه ای را ایجاد کند.
  • روش های Oneway داده ها را فقط در یک جهت ارسال می کنند و مسدود نمی شوند. اگر مقدار داده‌های حین پرواز در تماس‌های RPC از محدودیت‌های پیاده‌سازی فراتر رود، تماس‌ها ممکن است یک نشانه خطا را مسدود یا نشان دهند (رفتار هنوز مشخص نشده است).

متدی که مقداری را بر نمی گرداند اما به عنوان oneway اعلام نشده است همچنان مسدود است.

تمام متدهای اعلام شده در رابط HIDL در یک جهت فراخوانی می شوند، یا از HAL یا به HAL. این رابط مشخص نمی کند که در کدام جهت فراخوانی شود. معماری هایی که نیاز به فراخوانی دارند تا از HAL منشاء بگیرند، باید دو (یا بیشتر) اینترفیس را در بسته HAL ارائه کنند و رابط مناسب را از هر فرآیند ارائه دهند. کلمات کلاینت و سرور با توجه به جهت فراخوانی رابط استفاده می شود (یعنی HAL می تواند سرور یک رابط و مشتری یک رابط دیگر باشد).

تماس های تلفنی

کلمه callback به دو مفهوم مختلف اشاره دارد که با callback همزمان و callback ناهمزمان متمایز می شوند.

در برخی روش‌های HIDL که داده‌ها را برمی‌گردانند از تماس‌های همزمان استفاده می‌شود. یک روش HIDL که بیش از یک مقدار را برمی گرداند (یا یک مقدار از نوع غیر ابتدایی را برمی گرداند) نتایج خود را از طریق یک تابع فراخوانی برمی گرداند. اگر فقط یک مقدار برگردانده شود و از نوع اولیه باشد، از callback استفاده نمی شود و مقدار از متد برگردانده می شود. سرور متدهای HIDL را پیاده سازی می کند و کلاینت کال بک ها را پیاده سازی می کند.

تماس‌های ناهمزمان به سرور رابط HIDL اجازه می‌دهد تا تماس‌ها را آغاز کند. این کار با عبور یک نمونه از یک اینترفیس دوم از رابط اول انجام می شود. کلاینت رابط اول باید به عنوان سرور رابط دوم عمل کند. سرور رابط اول می تواند متدها را روی شی واسط دوم فراخوانی کند. به عنوان مثال، یک پیاده سازی HAL ممکن است اطلاعات را به طور ناهمزمان به فرآیندی که از آن استفاده می کند، با فراخوانی متدها بر روی یک شی رابط ایجاد شده و ارائه شده توسط آن فرآیند، ارسال کند. روش‌های موجود در رابط‌های مورد استفاده برای پاسخ به تماس ناهمزمان ممکن است مسدود شوند (و ممکن است مقادیر را به تماس‌گیرنده برگردانند) یا oneway باشند. به عنوان مثال، به «بازخوانی ناهمزمان» در HIDL C++ مراجعه کنید.

برای ساده‌سازی مالکیت حافظه، فراخوانی‌های متد و فراخوانی‌ها فقط پارامترها in می‌کنند و از پارامترهای out یا inout پشتیبانی نمی‌کنند.

محدودیت های هر معامله

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

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

پیاده سازی روش

HIDL فایل‌های هدر را تولید می‌کند که انواع، روش‌ها و تماس‌های لازم را در زبان مقصد (C++ یا جاوا) اعلام می‌کند. نمونه اولیه روش‌ها و فراخوان‌های تعریف‌شده توسط HIDL برای هر دو کد کلاینت و سرور یکسان است. سیستم HIDL پیاده‌سازی‌های پراکسی روش‌هایی را در سمت تماس‌گیرنده ارائه می‌کند که داده‌ها را برای انتقال IPC سازماندهی می‌کند، و کد خرد در سمت تماس گیرنده که داده‌ها را به پیاده‌سازی توسعه‌دهنده روش‌ها ارسال می‌کند.

فراخوان دهنده یک تابع (روش HIDL یا پاسخ به تماس) مالکیت ساختارهای داده منتقل شده به تابع را دارد و پس از فراخوانی مالکیت خود را حفظ می کند. در تمام موارد تماس گیرنده نیازی به آزاد کردن یا آزاد کردن فضای ذخیره سازی ندارد.

  • در C++، داده ها ممکن است فقط خواندنی باشند (تلاش برای نوشتن روی آن ممکن است باعث خطای بخش بندی شود) و برای مدت زمان تماس معتبر هستند. کلاینت می تواند داده ها را به صورت عمیق کپی کند تا فراتر از تماس منتشر شود.
  • در جاوا، کد یک کپی محلی از داده ها (یک شی معمولی جاوا) را دریافت می کند، که ممکن است آن را نگه دارد و تغییر دهد یا اجازه دهد که زباله جمع آوری شود.

انتقال داده غیر RPC

HIDL دو راه برای انتقال داده بدون استفاده از تماس RPC دارد: حافظه مشترک و صف پیام سریع (FMQ) که هر دو فقط در C++ پشتیبانی می شوند.

  • حافظه مشترک memory نوع HIDL داخلی برای ارسال یک شی که نشان دهنده حافظه مشترک اختصاص داده شده است استفاده می شود. می تواند در فرآیند دریافت برای نقشه برداری از حافظه مشترک استفاده شود.
  • صف پیام سریع (FMQ) . HIDL یک نوع صف پیام قالبی را ارائه می دهد که گذر پیام بدون انتظار را پیاده سازی می کند. از هسته یا زمان‌بندی در حالت عبور یا binderized استفاده نمی‌کند (ارتباط بین دستگاهی این ویژگی‌ها را نخواهد داشت). به طور معمول، HAL انتهای صف خود را تنظیم می کند، و یک شی ایجاد می کند که می تواند از طریق RPC از طریق پارامتری از نوع HIDL داخلی MQDescriptorSync یا MQDescriptorUnsync منتقل شود. این شی می تواند توسط فرآیند دریافت برای تنظیم انتهای دیگر صف استفاده شود.
    • صف های همگام سازی مجاز به سرریز شدن نیستند و فقط می توانند یک خواننده داشته باشند.
    • صف های Unsync مجاز به سرریز شدن هستند و می توانند خواننده های زیادی داشته باشند که هر کدام باید داده ها را به موقع بخوانند یا آن را از دست بدهند.
    به هیچ کدام از این‌ها اجازه داده نمی‌شود (خواندن از یک صف خالی با شکست مواجه می‌شود)، و هر نوع فقط می‌تواند یک نویسنده داشته باشد.

برای جزئیات بیشتر در مورد FMQ، به صف پیام سریع (FMQ) مراجعه کنید.