Arayüzler

HIDL paketinde tanımlanan her arayüzün, paketinin ad alanında kendi otomatik olarak oluşturulmuş C++ sınıfı vardır. İstemciler ve sunucular, arayüzlerle farklı şekillerde çalışır:

  • Sunucular arayüzleri uygular.
  • İstemciler, arayüzlerdeki yöntemleri çağırır.

Arayüzler sunucu tarafından ada göre kaydedilebilir veya HIDL tarafından tanımlanan yöntemlere parametre olarak iletilebilir. Örneğin, çerçeve kodu, HAL'den asenkron mesajlar almak için bir arayüz sunabilir ve bu arayüzü kaydetmeden doğrudan HAL'e iletebilir.

Sunucu uygulaması

IFoo arayüzünü uygulayan bir sunucu, otomatik olarak oluşturulan IFoo üstbilgi dosyasını içermelidir:

#include <android/hardware/samples/1.0/IFoo.h>

Başlık, bağlantı oluşturmak için IFoo arayüzünün paylaşılan kitaplığı tarafından otomatik olarak dışa aktarılır. Örnek IFoo.hal:

// IFoo.hal
interface IFoo {
    someMethod() generates (vec<uint32_t>);
    ...
}

IFoo arayüzünün sunucu uygulaması için örnek iskelet:

// From the IFoo.h header
using android::hardware::samples::V1_0::IFoo;

class FooImpl : public IFoo {
    Return<void> someMethod(foo my_foo, someMethod_cb _cb) {
        vec<uint32_t> return_data;
        // Compute return_data
        _cb(return_data);
        return Void();
    }
    ...
};

Bir sunucu arayüzünün uygulanmasını istemciye sunmak için şunları yapabilirsiniz:

  1. Arayüz uygulamasını hwservicemanager ile kaydedin (ayrıntılar aşağıda verilmiştir),

    VEYA

  2. Arayüz uygulamasını bir arayüz yönteminin bağımsız değişkeni olarak geçirin (ayrıntılar için Asenkron geri çağırma bölümüne bakın).

Arayüz uygulamasını kaydederken hwservicemanager işlemi, cihazda çalışan kayıtlı HIDL arayüzlerini ad ve sürüme göre izler. Sunucular, HIDL arayüz uygulamasını ada göre kaydedebilir ve istemciler, hizmet uygulamalarını ada ve sürüme göre isteyebilir. Bu işlem, HIDL arayüzünü sunarandroid.hidl.manager@1.0::IServiceManager.

Otomatik olarak oluşturulan her HIDL arayüz üstbilgi dosyasında (IFoo.h gibi), arayüz uygulamasını hwservicemanager ile kaydetmek için kullanılabilecek bir registerAsService() yöntemi bulunur. İstemciler daha sonra hwservicemanager'ten arayüzü almak için bu adı kullanacağından tek zorunlu bağımsız değişken, arayüz uygulamalarının adıdır:

::android::sp<IFoo> myFoo = new FooImpl();
::android::sp<IFoo> mySecondFoo = new FooAnotherImpl();
status_t status = myFoo->registerAsService();
status_t anotherStatus = mySecondFoo->registerAsService("another_foo");

hwservicemanager, farklı arayüzlerin (veya aynı arayüzün farklı sürümlerinin) aynı örnek adlarıyla çakışmadan kaydedilebilmesi için [package@version::interface, instance_name] kombinasyonunu benzersiz olarak değerlendirir. registerAsService()'yi tam olarak aynı paket sürümü, arayüz ve örnek adı ile çağırırsanız hwservicemanager, daha önce kayıtlı hizmete olan referansını bırakır ve yenisini kullanır.

İstemci uygulaması

Sunucu gibi, istemci de atıfta bulunduğu her arayüzü #include etmelidir:

#include <android/hardware/samples/1.0/IFoo.h>

Müşteriler iki şekilde arayüz elde edebilir:

  • I<InterfaceName>::getService aracılığıyla (hwservicemanager üzerinden)
  • Arayüz yöntemi aracılığıyla

Otomatik olarak oluşturulan her arayüz üstbilgi dosyasında, hwservicemanager'dan bir hizmet örneği almak için kullanılabilecek statik bir getServiceyöntemi bulunur:

// getService returns nullptr if the service can't be found
sp<IFoo> myFoo = IFoo::getService();
sp<IFoo> myAlternateFoo = IFoo::getService("another_foo");

Artık istemcinin bir IFoo arayüzü var ve yerel sınıf uygulamasıymış gibi bu arayüzün yöntemlerini çağırabilir. Gerçekte, uygulama aynı işlemde, farklı bir işlemde veya hatta başka bir cihazda (HAL uzaktan kumanda ile) çalışabilir. Müşteri, paketin 1.0 sürümünden itibaren dahil edilen bir IFoo nesnesinde getService'ü çağırdığından hwservicemanager, yalnızca bu uygulama 1.0 istemcileriyle uyumluysa bir sunucu uygulaması döndürür. Pratikte bu, yalnızca 1.n sürümüne sahip sunucu uygulamalarının (bir arayüzün x.(y+1) sürümü x.y'yi genişletmelidir (devralmalıdır)) geçerli olduğu anlamına gelir.

Ayrıca, farklı arayüzler arasında yayınlamak için castFrom yöntemi sağlanır. Bu yöntem, temel türün istenen türle aynı olduğundan emin olmak için uzak arayüze IPC çağrısı yaparak çalışır. İstenen tür kullanılamıyorsa nullptr döndürülür.

sp<V1_0::IFoo> foo1_0 = V1_0::IFoo::getService();
sp<V1_1::IFoo> foo1_1 = V1_1::IFoo::castFrom(foo1_0);

Eşzamansız geri çağırmalar

Mevcut HAL uygulamalarının çoğu eşzamansız donanımlarla iletişim kurar. Bu, istemcileri gerçekleşen yeni etkinlikler hakkında bilgilendirmek için eşzamansız bir yönteme ihtiyaç duydukları anlamına gelir. HIDL arayüz işlevleri, HIDL arayüz nesnelerini parametre olarak alabileceğinden HIDL arayüzü, asenkron geri çağırma olarak kullanılabilir.

Örnek arayüz dosyası IFooCallback.hal:

package android.hardware.samples@1.0;
interface IFooCallback {
    sendEvent(uint32_t event_id);
    sendData(vec<uint8_t> data);
}

IFoo sınıfında IFooCallback parametresi alan yeni yöntem örneği:

package android.hardware.samples@1.0;
interface IFoo {
    struct Foo {
       int64_t someValue;
       handle myHandle;
    };

    someMethod(Foo foo) generates (int32_t ret);
    anotherMethod() generates (vec<uint32_t>);
    registerCallback(IFooCallback callback);
};

IFoo arayüzünü kullanan istemci, IFooCallback arayüzünün sunucusudur ve IFooCallback'ın bir uygulamasını sağlar:

class FooCallback : public IFooCallback {
    Return<void> sendEvent(uint32_t event_id) {
        // process the event from the HAL
    }
    Return<void> sendData(const hidl_vec<uint8_t>& data) {
        // process data from the HAL
    }
};

Ayrıca, bu değeri IFoo arayüzünün mevcut bir örneğine de iletebilir:

sp<IFooCallback> myFooCallback = new FooCallback();
myFoo.registerCallback(myFooCallback);

IFoo özelliğini uygulayan sunucu, bunu bir sp<IFooCallback> nesnesi olarak alır. Geri çağırma işlevini saklayabilir ve bu arayüzü kullanmak istediğinde müşteriye geri çağrı gönderebilir.

Ölüm alıcıları

Hizmet uygulamaları farklı bir işlemde çalışabileceğinden, istemci çalışırken bir arayüzü uygulayan işlemin sona ermesi mümkündür. Devre dışı kalan bir süreçte barındırılan bir arayüz nesnesine yapılan tüm çağrılar aktarım hatasıyla başarısız olur (isOK(), false döndürür). Bu tür bir hatadan kurtulmanın tek yolu, I<InterfaceName>::getService() çağrısı yaparak hizmetin yeni bir örneğini istemektir. Bu yöntem yalnızca kilitlenen işlemin yeniden başlatılıp hizmetlerini servicemanager'e yeniden kaydettiği durumlarda işe yarar (bu durum genellikle HAL uygulamaları için geçerlidir).

Bu konuyu reaktif olarak ele almak yerine, bir arayüzün istemcileri, bir hizmet sonlandırıldığında bildirim almak için sonlandırma alıcısı da kaydedebilir. İstemci, alınan bir IFoo arayüzünde bu tür bildirimlere kaydolmak için aşağıdakileri yapabilir:

foo->linkToDeath(recipient, 1481 /* cookie */);

recipient parametresi, HIDL tarafından sağlanan android::hardware::hidl_death_recipient arayüzünün bir uygulaması olmalıdır. Bu uygulama, arayüzü barındıran işlem sona erdiğinde RPC iş parçacığı havuzundaki bir iş parçacığında çağrılan tek bir serviceDied() yöntemi içerir:

class MyDeathRecipient : public android::hardware::hidl_death_recipient {
    virtual void serviceDied(uint64_t cookie, const android::wp<::android::hidl::base::V1_0::IBase>& who) {
       // Deal with the fact that the service died
    }
}

cookie parametresi, linkToDeath() ile birlikte iletilen çerezi içerirken who parametresi, istemcide hizmeti temsil eden nesnenin zayıf bir işaretçisini içerir. Yukarıdaki örnek çağrıda cookie 1481'e, who ise foo'ye eşittir.

Kayıtlı bir ölüm alıcının kaydını da iptal edebilirsiniz:

foo->unlinkToDeath(recipient);