Friday, August 2, 2019

Android: Creating custom HIDL

HAL interface definition language(HIDL) is used to interface between HAL and its users. It creates a seamless way to invoke the HAL interface from either Java or cpp. All the inter process communication boiler plate code required is automatically created when the HIDL file is compiled.

This article outlines the method to create your custom HIDL. In this example HIDL interface is created under vendor instead of directly adding onto any other AOSP directories.

Create your hidl interface directory and the sub directories as shown below
vendor/mycompany/proprietary/interfaces/myhal/1.0/default

Now time to create your hidl interface file IMycompanyMyhal.hal

package vendor.mycompany.hardware.myhal@1.0;

interface IMycompanyMyhal {
  /**
   * Starts myhal data streaming
   *
   */
  oneway startMyhal();

  /**
   * Stops myhal data streaming
   *
   */
  oneway stopMyhal();

  /**
   * Gets current myhal state
   *
   */
  getCurrentState() generates (MycompanyMyhalState state);
};


Types needed for your interface should be defined in types.hal. In the interface defined above we use the type 'MycompanyMyhalState' as the type.

package vendor.mycompany.hardware.myhal@1.0;

enum MycompanyMyhalState : int32_t {
  IDLE = 0,
  PLAYING = 1,
  ERROR = 2
};


Create the makefiles Android.bp & Android.mk. You can also use hidl-gen tool to automatically create these files.

Android.bp file example

// This file is autogenerated by hidl-gen. Do not edit manually.

filegroup {
    name: "vendor.mycompany.hardware.myhal@1.0_hal",
    srcs: [
        "types.hal",
        "IMycompanyMyhal.hal",
    ],
}

genrule {
    name: "vendor.mycompany.hardware.myhal@1.0_genc++",
    tools: ["hidl-gen"],
    cmd: "$(location hidl-gen) -o $(genDir) -Lc++-sources \
       -randroid.hidl:system/libhidl/transport \
       -rvendor.mycompany.hardware:vendor/mycompany/proprietary/interfaces 
       vendor.mycompany.hardware.myhal@1.0",
    srcs: [
        ":vendor.mycompany.hardware.myhal@1.0_hal",
    ],
    out: [
        "vendor/mycompany/hardware/myhal/1.0/types.cpp",
        "vendor/mycompany/hardware/myhal/1.0/MycompanyMyhalAll.cpp",
    ],
}

genrule {
    name: "vendor.mycompany.hardware.myhal@1.0_genc++_headers",
    tools: ["hidl-gen"],
    cmd: "$(location hidl-gen) -o $(genDir) -Lc++-headers \
       -randroid.hidl:system/libhidl/transport \
       -rvendor.mycompany.hardware:vendor/mycompany/proprietary/interfaces \
       vendor.mycompany.hardware.myhal@1.0",
    srcs: [
        ":vendor.mycompany.hardware.myhal@1.0_hal",
    ],
    out: [
        "vendor/mycompany/hardware/myhal/1.0/types.h",
        "vendor/mycompany/hardware/myhal/1.0/hwtypes.h",
        "vendor/mycompany/hardware/myhal/1.0/IMycompanyMyhal.h",
        "vendor/mycompany/hardware/myhal/1.0/IHwMycompanyMyhal.h",
        "vendor/mycompany/hardware/myhal/1.0/BnHwMycompanyMyhal.h",
        "vendor/mycompany/hardware/myhal/1.0/BpHwMycompanyMyhal.h",
        "vendor/mycompany/hardware/myhal/1.0/BsMycompanyMyhal.h",
    ],
}

cc_library {
    name: "vendor.mycompany.hardware.myhal@1.0",
    defaults: ["hidl-module-defaults"],
    generated_sources: ["vendor.mycompany.hardware.myhal@1.0_genc++"],
    generated_headers: ["vendor.mycompany.hardware.myhal@1.0_genc++_headers"],
    export_generated_headers: ["vendor.mycompany.hardware.myhal@1.0_genc++_headers"],
    vendor_available: true,
    vndk: {
        enabled: true,
    },
    shared_libs: [
        "libhidlbase",
        "libhidltransport",
        "libhwbinder",
        "liblog",
        "libutils",
        "libcutils",
    ],

    export_shared_lib_headers: [
        "libhidlbase",
        "libhidltransport",
        "libhwbinder",
        "libutils",
    ],
}

cc_library {
    name: "vendor.mycompany.hardware.myhal@1.0_vendor",
    defaults: ["hidl-module-defaults"],
    generated_sources: ["vendor.mycompany.hardware.myhal@1.0_genc++"],
    generated_headers: ["vendor.mycompany.hardware.myhal@1.0_genc++_headers"],
    export_generated_headers: ["vendor.mycompany.hardware.myhal@1.0_genc++_headers"],
    vendor: true,
    shared_libs: [
        "libhidlbase",
        "libhidltransport",
        "libhwbinder",
        "liblog",
        "libutils",
        "libcutils",
    ],
    export_shared_lib_headers: [
        "libhidlbase",
        "libhidltransport",
        "libhwbinder",
        "libutils",
    ],
}

Android.mk file example
# This file is autogenerated by hidl-gen. Do not edit manually.

LOCAL_PATH := $(call my-dir)

################################################################################
include $(CLEAR_VARS)
LOCAL_MODULE := vendor.mycompany.hardware.myhal-V1.0-java
LOCAL_MODULE_CLASS := JAVA_LIBRARIES

intermediates := $(call local-generated-sources-dir, COMMON)

HIDL := $(HOST_OUT_EXECUTABLES)/hidl-gen$(HOST_EXECUTABLE_SUFFIX)

LOCAL_JAVA_LIBRARIES := \
    android.hidl.base-V1.0-java \

#
# Build types.hal (MycompanyMyhalState)
#
GEN := $(intermediates)/vendor/mycompany/hardware/myhal/V1_0/MycompanyMyhalState.java
$(GEN): $(HIDL)
$(GEN): PRIVATE_HIDL := $(HIDL)
$(GEN): PRIVATE_DEPS := $(LOCAL_PATH)/types.hal
$(GEN): PRIVATE_OUTPUT_DIR := $(intermediates)
$(GEN): PRIVATE_CUSTOM_TOOL = \
        $(PRIVATE_HIDL) -o $(PRIVATE_OUTPUT_DIR) \
        -Ljava \
        -randroid.hidl:system/libhidl/transport \
        -rvendor.mycompany.hardware:vendor/mycompany/proprietary/interfaces \
        vendor.mycompany.hardware.myhal@1.0::types.MycompanyMyhalState

$(GEN): $(LOCAL_PATH)/types.hal
 $(transform-generated-source)
LOCAL_GENERATED_SOURCES += $(GEN)

#
# Build IMycompanyMyhal.hal
#
GEN := $(intermediates)/vendor/mycompany/hardware/myhal/V1_0/IMycompanyMyhal.java
$(GEN): $(HIDL)
$(GEN): PRIVATE_HIDL := $(HIDL)
$(GEN): PRIVATE_DEPS := $(LOCAL_PATH)/IMycompanyMyhal.hal
$(GEN): PRIVATE_DEPS += $(LOCAL_PATH)/types.hal
$(GEN): $(LOCAL_PATH)/types.hal
$(GEN): PRIVATE_OUTPUT_DIR := $(intermediates)
$(GEN): PRIVATE_CUSTOM_TOOL = \
        $(PRIVATE_HIDL) -o $(PRIVATE_OUTPUT_DIR) \
        -Ljava \
        -randroid.hidl:system/libhidl/transport \
        -rvendor.mycompany.hardware:vendor/mycompany/proprietary/interfaces \
        vendor.mycompany.hardware.myhal@1.0::IMycompanyMyhal

$(GEN): $(LOCAL_PATH)/IMycompanyMyhal.hal
 $(transform-generated-source)
LOCAL_GENERATED_SOURCES += $(GEN)
include $(BUILD_JAVA_LIBRARY)

################################################################################

include $(CLEAR_VARS)
LOCAL_MODULE := vendor.mycompany.hardware.myhal-V1.0-java-static
LOCAL_MODULE_CLASS := JAVA_LIBRARIES

intermediates := $(call local-generated-sources-dir, COMMON)

HIDL := $(HOST_OUT_EXECUTABLES)/hidl-gen$(HOST_EXECUTABLE_SUFFIX)

LOCAL_STATIC_JAVA_LIBRARIES := \
    android.hidl.base-V1.0-java-static \

#
# Build types.hal (MycompanyMyhalState)
#
GEN := $(intermediates)/vendor/mycompany/hardware/myhal/V1_0/MycompanyMyhalState.java
$(GEN): $(HIDL)
$(GEN): PRIVATE_HIDL := $(HIDL)
$(GEN): PRIVATE_DEPS := $(LOCAL_PATH)/types.hal
$(GEN): PRIVATE_OUTPUT_DIR := $(intermediates)
$(GEN): PRIVATE_CUSTOM_TOOL = \
        $(PRIVATE_HIDL) -o $(PRIVATE_OUTPUT_DIR) \
        -Ljava \
        -randroid.hidl:system/libhidl/transport \
        -rvendor.mycompany.hardware:vendor/mycompany/proprietary/interfaces \
        vendor.mycompany.hardware.myhal@1.0::types.MycompanyMyhalState

$(GEN): $(LOCAL_PATH)/types.hal
 $(transform-generated-source)
LOCAL_GENERATED_SOURCES += $(GEN)

#
# Build IMycompanyMyhal.hal
#
GEN := $(intermediates)/vendor/mycompany/hardware/myhal/V1_0/IMycompanyMyhal.java
$(GEN): $(HIDL)
$(GEN): PRIVATE_HIDL := $(HIDL)
$(GEN): PRIVATE_DEPS := $(LOCAL_PATH)/IMycompanyMyhal.hal
$(GEN): PRIVATE_DEPS += $(LOCAL_PATH)/types.hal
$(GEN): $(LOCAL_PATH)/types.hal
$(GEN): PRIVATE_OUTPUT_DIR := $(intermediates)
$(GEN): PRIVATE_CUSTOM_TOOL = \
        $(PRIVATE_HIDL) -o $(PRIVATE_OUTPUT_DIR) \
        -Ljava \
        -randroid.hidl:system/libhidl/transport \
        -rvendor.mycompany.hardware:vendor/mycompany/proprietary/interfaces \
        vendor.mycompany.hardware.myhal@1.0::IMycompanyMyhal

$(GEN): $(LOCAL_PATH)/IMycompanyMyhal.hal
 $(transform-generated-source)
LOCAL_GENERATED_SOURCES += $(GEN)
include $(BUILD_STATIC_JAVA_LIBRARY)


Now moving onto the real deal, implement the interface. Our default implementation will be under '1.0/default' directory.

First we need to create a main function where the service is started, create 'service.cpp'.

#include <android-base/logging.h>
#include <hidl/HidlTransportSupport.h>
#include <utils/Looper.h>
#include <utils/StrongPointer.h>

#include "myhal.h"

using android::hardware::configureRpcThreadpool;
using android::hardware::joinRpcThreadpool;

int main(int /*argc*/, char** argv) {
  android::base::InitLogging(argv,
                             android::base::LogdLogger(android::base::SYSTEM));
  LOG(INFO) << "Mycompany myhal service booting up...";

  configureRpcThreadpool(1, true /* callerWillJoin */);

  // Setup hwbinder service
  android::sp<vendor::mycompany::hardware::myhal::V1_0::IMycompanyMyhal> service =
      new vendor::mycompany::hardware::myhal::V1_0::MycompanyMyhal();
  CHECK_EQ(service->registerAsService(), android::NO_ERROR)
      << "Failed to register Mycompany myhal service";

  joinRpcThreadpool();

  LOG(INFO) << "Mycompany myhal service is terminating...";
  return 0;
}



Now for the actual implementation of the hal interface
Header file myhal.h

#ifndef MYHAL_H_
#define MYHAL_H_

#include <functional>

#include <android-base/macros.h>
#include <vendor/mycompany/hardware/myhal/1.0/IMycompanyMyhal.h>
#include <utils/Looper.h>

#include "AudioEngine.h"

namespace vendor {
namespace mycompany {
namespace hardware {
namespace myhal {
namespace V1_0 {
using namespace vendor::mycompany::hardware::myhal::V1_0;
using namespace android::hardware;

/**
 * Root HIDL interface object used to control the Myhal HAL.
 */
class MycompanyMyhal : public V1_0::IMycompanyMyhal {
 public:
  MycompanyMyhal();

  //HIDL interfaces
  Return<void> startMyhal() override;
  Return<void> stopMyhal() override;
  Return<MycompanyMyhalState> getMyhalState() override;

 private:
  std::unique_ptr<AudioEngine> myhalEngine_;
  MycompanyMyhalState myhalState_;

  DISALLOW_COPY_AND_ASSIGN(MycompanyMyhal);
};

}  // namespace V1_0
}  // namespace myhal
}  // namespace hardware
}  // namespace mycompany
}  // namespace android

#endif  // MYHAL_H_

Interface implementation in myhal.cpp.

#include <android-base/logging.h>

#include "myhal.h"

namespace vendor {
namespace mycompany {
namespace hardware {
namespace myhal {
namespace V1_0 {

MycompanyMyhal::MycompanyMyhal() : myhalState_(MycompanyMyhalState::IDLE) {
  /* TODO: Do initialization here */
}

Return<void> MycompanyMyhal::startMyhal() {
  /* TODO: Do your logic to start myhal */
  myhalState_ = MycompanyMyhalState::PLAYING;
  return Void();
}

Return<void> MycompanyMyhal::stopMyhal() {
  /* TODO: Do your logic to stop myhal */
  myhalState_ = MycompanyMyhalState::IDLE;
  return Void();
}

Return<MycompanyMyhalState> MycompanyMyhal::getMyhalState() {
  return myhalState_;
}

}  // namespace V1_0
}  // namespace myhal
}  // namespace hardware
}  // namespace mycompany
}  // namespace android


Every service needs an '.rc' file to specify the service name, permissions etc and to start your service automatically - vendor.mycompany.hardware.myhal@1.0-service.rc
service mycompanymyhalservice /vendor/bin/hw/vendor.mycompany.hardware.myhal@1.0-service
    class hal
    user root
    group audio myhal system
    seclabel u:r:shell:s0


We need a makefile to compile this default implementation, so create Android.mk here to compile the implementation
LOCAL_PATH := $(call my-dir)

include $(CLEAR_VARS)
LOCAL_MODULE := vendor.mycompany.hardware.myhal@1.0-service
LOCAL_MODULE_RELATIVE_PATH := hw
LOCAL_PROPRIETARY_MODULE := true
LOCAL_CPPFLAGS := -Wall -Werror -Wextra
LOCAL_SRC_FILES := \
    myhal.cpp \
    service.cpp

LOCAL_C_INCLUDES := \
    frameworks/av/media/libaaudio/include

LOCAL_SHARED_LIBRARIES := \
    vendor.mycompany.hardware.myhal@1.0 \
    libbase \
    libcutils \
    libhidlbase \
    libhidltransport \
    libaaudio \
    liblog \
    libnl \
    libutils

LOCAL_INIT_RC := vendor.mycompany.hardware.myhal@1.0-service.rc
include $(BUILD_EXECUTABLE)



Android use SELINUX policies to determine which processes to run in the system. To run your service SELINUX permissions needs to be enabled
Most sepolicy is built under system/sepolicy.
But your vendor could override it under device/vendorname/sepolicy/common

Since this is hw service add the component in 'hwservice_contexts' file
vendor.mycompany.hardware.myhal::IMycompanyMyhal        u:object_r:hal_telephony_hwservice:s0

If application needs to find the HIDL interface it needs to be published in manifest file. Add it in one of the manifest.xml file for eg: device/<vendorname>/<platform>/manifest.xml
<!-- Mycompany service -->
    <hal format="hidl">
        <name>vendor.mycompany.hardware.myhal</name>
        <transport>hwbinder</transport>
        <impl level="generic"></impl>
        <version>1.0</version>
        <interface>
            <name>IMycompanyMyhal</name>
            <instance>default</instance>
        </interface>
    </hal>


If SELINUX mode is 'permissive' this should work. If not, to allow our service to run, add 'allow' line in hwservice.te.

After compilation make sure your service is copied to /vendor/bin/hw and the '.rc' file is copied to /vendor/etc/init.
Also your service library('.so') file should be copied to /system/lib and /system/lib64.


No comments:

Post a Comment