Android上层与驱动交互完整篇(三)HIDL服务
1、服务器端
网上很多教程是在hardware/interface下创建我们的hidl服务,其实各个厂商在设计hidl服务的时候,会放在vendor目录下。这样的好处就是,在不同平台适配时,可以直接平移我们的这部分代码。今天我们就在vendor下创建我们的hidl服务。
cd vendor; mkdir -p ./max/hardware/interfaces/idemo/1.0
idemo即是我们要创建的hidl 服务的目录。
interfaces目录下还要创建几个文件。
1、current.txt 这里会记录各个.hal文件的hashkey的值。假如你修改了hal文件,但是current.txt没有更新,编译时会报错。
2、Android.bp
hidl_package_root {
name: "vendor.max.hardware",
path: "vendor/max/hardware/interfaces",
}
// This is an autogenerated file, do not edit.
subdirs = [
"idemo/1.0", //假如interfaces多个hidl服务目录,这里要写多个。
]
3、update-base.sh
#!/bin/bash
# WARNING: vendor-base is placed here only for testing.
# It should typically be created elsewhere.
options="-Lexport-header \
-r vendor.max.hardware:vendor/max/hardware/interfaces \
-r android.hidl:system/libhidl/transport\
-r android.hardware:hardware/interfaces"
hidl-gen $options \
-o system/tools/hidl/test/vendor/1.0/vendor-base.h \
vendor.max.hardware.idemo@1.0
4 、update-makefiles.sh
#!/bin/bash
source system/tools/hidl/update-makefiles-helper.sh
do_makefiles_update \
"vendor.max.hardware:vendor/max/hardware/interfaces" \
"android.hidl:system/libhidl/transport" \
"android.hardware:hardware/interfaces"
5、在idemo/1.0 下创建IDemo.hal types.hal IDemoCallback.hal三个文件内容如下
IDemo .hal
package vendor.max.hardware.idemo@1.0;
import IDemoCallback;
interface IDemo{
hwInvoke(string request) generates (string ret);
hwRegistCallback(IDemoCallback callback) generates (int32_t ret);
};
types.hal
package vendor.max.hardware.idemo@1.0;
enum Result : uint32_t {
OK, // Success
FAIL, // Failure, unknown reason
};
IDemoCallback.hal
package vendor.max.hardware.idemo@1.0;
interface IDemoCallback {
oneway hwNotify(uint32_t type, string reply);
};
6、输入命令生成cpp 以及Android.bp文件,假如源码已经编译过,而且已输入souce build/env_setup.sh, lunch命令
PACKAGE=vendor.max.hardware.idemo@1.0
LOC=vendor/max/hardware/interfaces/idemo/1.0/default/
hidl-gen -o $LOC -Lc++-impl -rvendor.max.hardware:vendor/max/hardware/interfaces -randroid.hidl:system/libhidl/transport $PACKAGE
hidl-gen -o $LOC -Landroidbp-impl -rvendor.max.hardware:vendor/max/hardware/interfaces -randroid.hidl:system/libhidl/transport $PACKAGE
7 、生成对应的hash 值,全部copy到current.txt中
hidl-gen -L hash -rvendor.max.hardware:vendor/max/hardware/interfaces -randroid.hidl:system/libhidl/transport $PACKAGE
8、这样,1.0/default目录下会生成.cpp文件,但是我不习惯把service放在这里编译,相反我会将server代码与client代码放在一起编译,所有这里default目录对于我来说是没用的。
只看1.0目录下的Android.bp即可。
// This file is autogenerated by hidl-gen -Landroidbp.
hidl_interface {
name: "vendor.max.hardware.idemo@1.0",
root: "vendor.max.hardware",
vendor: true,
srcs: [
"types.hal",
"IDemo.hal",
"IDemoCallback.hal",
],
interfaces: [
"android.hidl.base@1.0",
],
types: [
"Result",
],
gen_java: true,
}
此bp文件将三个hal文件编译成vendor.max.hardware.idemo@1.0.so,
目录位于/vendor/lib下,编译命令如下:
mmm vendor/max/interfaces/idemo -j32
至此interfaces目录创建完成。
接下来在源码编译app的地方,创建我们的应用或者我们中间件目录,命名为Demo,命令如下
mkdir -p ./Demo/service
Demo目录下创建Android.mk文件
# Call sub android mks
LOCAL_PATH:= $(call my-dir)
include $(call all-makefiles-under,$(LOCAL_PATH))
service目录下创建五个文件
touch main_service.cpp
touch DemoImpl.cpp
touch DemoImpl.h
touch android.mk
touch demo.rc
DemoImpl.h其实对应的是interface/1.0/default/自动生成的Demo.h文件,我们可以参考进行修改。
#ifndef VENDOR_MAX_HARDWARE_IDEMO_V1_0_IDEMO_H
#define VENDOR_MAX_HARDWARE_IDEMO_V1_0_IDEMO_H
#include <vendor/max/hardware/idemo/1.0/IDemo.h>
#include <hidl/MQDescriptor.h>
#include <hidl/Status.h>
#include <log/log.h>
namespace vendor {
namespace max {
namespace hardware {
namespace idemo {
namespace V1_0 {
namespace implementation {
using ::android::hardware::hidl_array;
using ::android::hardware::hidl_memory;
using ::android::hardware::hidl_string;
using ::android::hardware::hidl_vec;
using ::android::hardware::Return;
using ::android::hardware::Void;
using ::android::sp;
class DemoImpl : public IDemo {
public:
// Methods from ::vendor::max::hardware::idemo::V1_0::IDemo follow.
Return<void> hwInvoke(const hidl_string& request, hwInvoke_cb _hidl_cb) override;
Return<int32_t> hwRegistCallback(const sp<::vendor::max::hardware::idemo::V1_0::IDemoCallback>& callback) override;
// Methods from ::android::hidl::base::V1_0::IBase follow.
private:
mutable android::Mutex mLock;
std::map<uint32_t, sp<IDemoCallback>> mClients;
};
// FIXME: most likely delete, this is only for passthrough implementations
// extern "C" IHrtvMw* HIDL_FETCH_IHrtvMw(const char* name);
} // namespace implementation
} // namespace V1_0
} // namespace idemo
} // namespace hardware
} // namespace max
} // namespace vendor
#endif // VENDOR_MAX_HARDWARE_IDEMO_V1_0_IDEMO_H
DemoImpl.cpp 其实对应的是interface/1.0/default/自动生成的Demo.cpp文件,这里我们也是自己去仿照完成。
#define LOG_TAG "Max@DemoImpl.cpp"
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <errno.h>
#include <stdint.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#include <binder/ProcessState.h>
#include <binder/IPCThreadState.h>
#include <inttypes.h>
#include <dlfcn.h>
#include <hwbinder/Parcel.h>
#include <atomic>
#include <hardware/hardware.h>
#include <utils/threads.h>
#include <hidl/HidlSupport.h>
#include <hidl/HidlTransportSupport.h>
#include "HrtvMwImpl.h"
namespace vendor {
namespace max {
namespace hardware {
namespace idemo {
namespace V1_0 {
namespace implementation {
// Methods from ::vendor::max::hardware::idemo::V1_0::IDemo follow.
Return<void> DemoImpl::hwInvoke(const hidl_string& request, hwInvoke_cb _hidl_cb) {
// TODO implement
_hidl_cb(request); //这里将传入的参数直接返回
return Void();
}
Return<int32_t> DemoImpl::hwRegistCallback(const sp<::vendor::max::hardware::idemo::V1_0::IDemoCallback>& callback) {
// TODO implement
android::AutoMutex _l( mLock );
if (callback != nullptr) {
int cookie = -1;
int clientSize = mClients.size();
for (int i = 0; i < clientSize; i++) {
if (mClients[i] == nullptr) {
ALOGI("%s, client index:%d had died, this id give the new client", __FUNCTION__, i);
cookie = i;
mClients[i] = callback;
break;
}
}
if (cookie < 0) {
cookie = clientSize;
mClients[clientSize] = callback;
}
ALOGI("%s cookie:%d, client size:%d", __FUNCTION__, cookie, (int)mClients.size());
}
return int32_t {};
}
// Methods from ::android::hidl::base::V1_0::IBase follow.
} // namespace implementation
} // namespace V1_0
} // namespace idemo
} // namespace hardware
} // namespace max
} // namespace vendor
Android.mk
LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE:= demo
LOCAL_INIT_RC := demo.rc
LOCAL_SRC_FILES:= \
DemoImpl.cpp \
main_service.cpp
LOCAL_CFLAGS += -DHDCP_AUTHENTICATION
LOCAL_CPPFLAGS += -std=c++14
LOCAL_CFLAGS += -DANDROID_PLATFORM_SDK_VERSION=$(PLATFORM_SDK_VERSION)
LOCAL_SHARED_LIBRARIES := \
libcutils \
libutils \
liblog \
libbinder \
libhwbinder \
libbase \
libm \
libhidlbase \
libhidltransport \
vendor.max.hardware.idemo@1.0
LOCAL_C_INCLUDES := \
external/zlib \
external/libcxx/include \
system/core/include \
system/libhidl/transport/include/hidl \
external/googletest/googletest/include \
hardware/libhardware/include \
LOCAL_CFLAGS += -D__ANDROID_O
LOCAL_CFLAGS += -Wno-unused-variable
LOCAL_CFLAGS += -Wno-sign-compare
LOCAL_CFLAGS += -Wno-pointer-sign
LOCAL_CFLAGS += -Wno-unused-function
LOCAL_CFLAGS += -Wno-unused-parameter
LOCAL_CFLAGS += -Wno-unused-variable
LOCAL_CFLAGS += -Wno-implicit-function-declaration
LOCAL_CFLAGS += -Wno-unused-result
ifeq ($(shell test $(PLATFORM_SDK_VERSION) -ge 26 && echo OK),OK)
LOCAL_PROPRIETARY_MODULE := true
endif
include $(BUILD_EXECUTABLE)
demo.rc
on early-boot
start demo
service demo /vendor/bin/demo
user root
group system root media audio
main_service.cpp
#define LOG_TAG "Max@Main_Service.cpp"
#include <vendor/max/hardware/idemo/1.0/IDemo.h>
#include <binder/IPCThreadState.h>
#include <binder/ProcessState.h>
#include <binder/IServiceManager.h>
#include <cutils/properties.h>
#include <HidlTransportSupport.h>
#include "DemoImpl.h"
using namespace android;
using ::android::hardware::configureRpcThreadpool;
using ::vendor::max::hardware::idemo::V1_0::implementation::DemoImpl;
using ::vendor::max::hardware::idemo::V1_0::IDemo;
int main()
{
android::ProcessState::initWithDriver("/dev/vndbinder");
configureRpcThreadpool(16, false);
sp<ProcessState> proc(ProcessState::self());
sp<IDemo> demo = new DemoImpl();
if (demo == nullptr) {
ALOGE("Cannot create IDemo service");
} else if (demo->registerAsService() != OK) {
ALOGE("Cannot register IDemo service.");
} else {
ALOGI("Treble IDemo service created.");
}
/*
* This thread is just going to process Binder transactions.
*/
IPCThreadState::self()->joinThreadPool();
}
mmm Demo/service -j32
生成vendor/bin/demo以及/vendor/etc/init/demo.rc
至此 service已经搭建完成。
2、客户端
分两种 一种是java,一种是c++。
先讲java代码
这里将在源码下封装一个jar包,提供app调用。
先在上面Demo目录下建立java以及包名目录
mkidir -p java/com/mac/demo
cd java
touch Android.mk
touch com.max.demo.xml
cd java/com/max/demo/
touch DemoManager.java
Android.mk
LOCAL_PATH:= $(call my-dir)
# the library
# ============================================================
include $(CLEAR_VARS)
LOCAL_SRC_FILES := \
$(call all-subdir-java-files)
LOCAL_MODULE := Demo
LOCAL_MODULE_CLASS := JAVA_LIBRARIES
LOCAL_DX_FLAGS := --core-library
ifeq ($(shell test $(PLATFORM_SDK_VERSION) -ge 26 && echo OK),OK)
LOCAL_PROPRIETARY_MODULE := true
endif
LOCAL_JAVA_LIBRARIES := \
android.hidl.base-V1.0-java \
android.hidl.manager-V1.0-java
LOCAL_STATIC_JAVA_LIBRARIES := \
vendor.max.hardware.idemo-V1.0-java
include $(BUILD_JAVA_LIBRARY)
#copy xml to permissions directory
include $(CLEAR_VARS)
LOCAL_MODULE := com.max.demo.xml
LOCAL_MODULE_TAGS := optional
LOCAL_MODULE_CLASS := ETC
LOCAL_SRC_FILES := $(LOCAL_MODULE)
ifeq ($(shell test $(PLATFORM_SDK_VERSION) -ge 26 && echo OK),OK)
LOCAL_PROPRIETARY_MODULE := true
LOCAL_MODULE_PATH := $(TARGET_OUT_VENDOR)/etc/permissions
else
LOCAL_MODULE_PATH := $(TARGET_OUT_ETC)/permissions
endif
include $(BUILD_PREBUILT)
com.max.demo.xml
<?xml version="1.0" encoding="utf-8"?>
<permissions>
<library name="com.max.demo"
file="/vendor/framework/Demo.jar"/>
</permissions>
DemoManager.java
package com.max.demo;
import android.content.Context;
import android.os.IBinder;
import android.os.HwBinder;
import android.os.Parcel;
import android.os.RemoteException;
import android.util.Log;
import java.util.ArrayList;
import java.util.NoSuchElementException;
import vendor.max.hardware.idemoV1_0.IDemo;
import vendor.max.hardware.idemo.V1_0.IDemoCallback;
import vendor.max.hardware.idemo.V1_0.Result;
public class DemoManager {
public static final String TAG = "DemoManager";
private static DemoManager mInstance;
private IDemo mProxy = null;
// Mutex for all mutable shared state.
private final Object mLock = new Object();
public DemoManager(){
LogUtil.d(TAG,"DemoManager");
connectToProxy();
}
private void connectToProxy() {
synchronized (mLock) {
if (mProxy != null) {
return;
}
try {
mProxy = IDemo.getService();
} catch (RemoteException e) {
LogUtil.e(TAG, "connectToProxy: idemo service not responding", e);
}
}
}
// 对应interfaces里的hwinvok函数
public String hwInvoke(string demo) {
synchronized (mLock) {
return mProxy.hwInvoke(demo);
}
}
// 如果hidl服务里 有两个返回值可以用这种方法获取。
//public String getBuildTime() {
//synchronized (mLock) {
//Mutable<String> resultVal = new Mutable<>();
//try {
// mProxy.getBuildTime((int ret, String v) -> {
//if (Result.OK == ret) {
//resultVal.value = v;
// }
//});
// return resultVal.value;
// } catch (RemoteException e) {
// Log.e(TAG, "getBuildTime:" + e);
// }
// }
//return "";
//}
private static class Mutable<E> {
public E value;
Mutable() {
value = null;
}
Mutable(E value) {
this.value = value;
}
}
}
mmm Demo/java -j32
在vendor/framework/生成Demo.jar
app引用jar时候,要将源吗里的copy出来。
应在manifest.xml添加jar的引用。
2、c++
Demo目录下建立cplusplus目录
Android.mk
LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE:= democlient
LOCAL_SRC_FILES:= \
DemoClient.cpp
LOCAL_CFLAGS += -DHDCP_AUTHENTICATION
LOCAL_CPPFLAGS += -std=c++14
LOCAL_CFLAGS += -DANDROID_PLATFORM_SDK_VERSION=$(PLATFORM_SDK_VERSION)
LOCAL_SHARED_LIBRARIES := \
libcutils \
libutils \
liblog \
libbinder \
libhwbinder \
libbase \
libm \
libhidlbase \
libhidltransport \
vendor.max.hardware.idemo@1.0
LOCAL_C_INCLUDES := \
external/zlib \
external/libcxx/include \
system/core/include \
system/libhidl/transport/include/hidl \
external/googletest/googletest/include \
hardware/libhardware/include \
LOCAL_CFLAGS += -D__ANDROID_O
LOCAL_CFLAGS += -Wno-unused-variable
LOCAL_CFLAGS += -Wno-sign-compare
LOCAL_CFLAGS += -Wno-pointer-sign
LOCAL_CFLAGS += -Wno-unused-function
LOCAL_CFLAGS += -Wno-unused-parameter
LOCAL_CFLAGS += -Wno-unused-variable
LOCAL_CFLAGS += -Wno-implicit-function-declaration
LOCAL_CFLAGS += -Wno-unused-result
ifeq ($(shell test $(PLATFORM_SDK_VERSION) -ge 26 && echo OK),OK)
LOCAL_PROPRIETARY_MODULE := true
endif
include $(BUILD_EXECUTABLE)
DemoClient.cpp
#define LOG_TAG "DemoClient.cpp"
#include <vendor/max/hardware/idemo/1.0/IDemo.h>
#include <hidl/LegacySupport.h>
#include <log/log.h>
#include <stdio.h>
using android::sp;
using vnedor::max::hardware::idemo::V1_0::IDemo;
using ::android::hardware::Return;
using ::android::hardware::hidl_string;
using ::android::hardware::Void;
int main(){
android::sp<IDemo> hw_device = IDemo::getService();
if (hw_device == nullptr) {
ALOGD("...failed to get hello-hidl");
return -1;
}
ALOGD("Max ...success to get hello-hidl....");
hidl_string demo = "hello world";
hw_device->hwInvoke(demo,[&](hidl_string result){
ALOGD("Max ...success hwInvoke....%s",result.c_str());
});
}
编译后/vendor/bin/下生成democlient。
以上所有代码都是写文章时写的,并没有真正在源码里编译,如有编译错误,可自行修改。
以上就可以做到app调用hidl服务了,但是如果有很多个函数,是不是hal文件里要定义很多个函数,每次的修改都很复杂,两边都要同步修改。
那么,下篇文章讲解如何用hwInvoke一个函数管理无数个函数的调用。
例如
client传一个函数的id
servce端接收到id后去执行id对应的函数。