提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档
文章目录
前言
最近做的一个东西,需要跟硬件通讯,给上层app提供接口,因为涉及定制的硬件比较多,但是业务逻辑不复杂,只需要简单的通讯,就单独做成一个模块,专门用于这些业务逻辑不复杂的硬件通讯。具体的修改还是参照之前的那一篇C++ HIDL,具体路径如下:
https://blog.csdn.net/jamecer/article/details/123652457
因为只使用一个服务进行启动及处理,代码中有关线程方面可能会有一些坑,如果有更好的方法,谢谢指正。
一、有关selinux的设置
这里就不先从cpp等文件的设置开始了,直接先从selinux的添加开始
之前的安全并未对这些语句进行解释,现在对这些语句进行解释
具体可见如下博客,对selinux的一些说明
https://blog.csdn.net/xxdw1992/article/details/120881548
https://blog.csdn.net/luoshengyang/article/details/38054645
https://blog.csdn.net/ldswfun/article/details/125284523
https://blog.csdn.net/qidi_huang/article/details/107472617
1-1 system/sepolicy/vendor目录
在/system/sepolicy/vendor/中新建hal_devicesServer_default.te
//这段代码表示 将 hal_devicesServer_default 声明为一个进程
//至于domain这个属性可以做什么事情,在system/sepolicy/public/domain.te中可知
type hal_devicesServer_default, domain;
/*下面这几个语句,是使用hal_server_domain这一个宏,
本质上跟上面那个语句的语法是一样的,
具体做了什么事情,我们下面进行分析,
这个宏是在system/sepolicy/public/te_macros中*/
/* 具体用法及功能,在文件中有详细说明
define(`hal_server_domain', `
typeattribute $1 halserverdomain;
typeattribute $1 $2_server;
typeattribute $1 $2;')
*/
/*将参数带入,可见如下声明(具体声明的意义,见上面提到的两篇博客):
typeattribute hal_devicesServer_default halserverdomain;
typeattribute hal_devicesServer_default hal_deviceA_server;
typeattribute hal_devicesServer_default hal_deviceA;
*/
hal_server_domain(hal_devicesServer_default, hal_deviceA)
hal_server_domain(hal_devicesServer_default, hal_deviceB)
hal_server_domain(hal_devicesServer_default, hal_deviceC)
//继续做类型声明
type hal_devicesServer_default_exec, exec_type, vendor_file_type, file_type;
/*同样见te_macros中:
define(`init_daemon_domain', `
domain_auto_trans(init, $1_exec, $1)
')
#执行了如下:
domain_auto_trans(init, hal_devicesServer_default_exec, hal_devicesServer_default)
define(`domain_auto_trans', `
# Allow the necessary permissions.
domain_trans($1,$2,$3)
# Make the transition occur by default.
type_transition $1 $2:process $3;
')
#执行了如下
domain_trans(init, hal_devicesServer_default_exec, hal_devicesServer_default)
type_transition init hal_devicesServer_default_exec:process hal_devicesServer_default;
define(`domain_trans', `
# Old domain may exec the file and transition to the new domain.
allow $1 $2:file { getattr open read execute map };
allow $1 $3:process transition;
# New domain is entered by executing the file.
allow $3 $2:file { entrypoint open read execute getattr map };
# New domain can send SIGCHLD to its caller.
ifelse($1, `init', `', `allow $3 $1:process sigchld;')
# Enable AT_SECURE, i.e. libc secure mode.
dontaudit $1 $3:process noatsecure;
# XXX dontaudit candidate but requires further study.
allow $1 $3:process { siginh rlimitinh };
')
执行了如下:
dontaudit init hal_devicesServer_default:process noatsecure;
allow init hal_devicesServer_default:process { siginh rlimitinh };
*/
init_daemon_domain(hal_devicesServer_default)
在system/sepolicy/vendor/file_contexts中添加如下
/*将vendor.mediatek.hardware.devicesServer@1.0-service这个服务,
授予hal_devicesServer_default_exec相关的权限,即该服务是一个可执行的安全对象
*/
/(vendor|system/vendor)/bin/hw/vendor\.mediatek\.hardware\.devicesServer@1\.0-service u:object_r:hal_devicesServer_default_exec:s0
1-2 system/sepolicy/public和/system/sepolicy/prebuilts目录
system/sepolicy/public/hal_devicesServer.te
# HwBinder IPC from client to server, and callbacks
# deviceA
binder_call(hal_deviceA_client, hal_deviceA_server)
binder_call(hal_deviceA_server, hal_deviceA_client)
add_hwservice(hal_deviceA_server, hal_deviceA_hwservice)
allow hal_deviceA_client hal_deviceA_hwservice:hwservice_manager find;
/*
#####################################
# binder_call(clientdomain, serverdomain)
# Allow clientdomain to perform binder IPC to serverdomain.
define(`binder_call', `
# Call the server domain and optionally transfer references to it.
allow $1 $2:binder { call transfer };
# Allow the serverdomain to transfer references to the client on the reply.
allow $2 $1:binder transfer;
# Receive and use open files from the server.
allow $1 $2:fd use;
')
执行如下:
allow hal_deviceA_client hal_deviceA_server:binder { call transfer };
allow hal_deviceA_server hal_deviceA_client:binder transfer;
allow hal_deviceA_client hal_deviceA_server:fd use;
define(`add_hwservice', `
allow $1 $2:hwservice_manager { add find };
allow $1 hidl_base_hwservice:hwservice_manager add;
neverallow { domain -$1 } $2:hwservice_manager add;
')
执行如下:
allow hal_deviceA_server hal_deviceA_hwservice:hwservice_manager { add find };
allow hal_deviceA_server hidl_base_hwservice:hwservice_manager add;
neverallow { domain -hal_deviceA_server } hal_deviceA_hwservice:hwservice_manager add;
*/
# deviceB
binder_call(hal_deviceB_client, hal_deviceB_server)
binder_call(hal_deviceB_server, hal_deviceB_client)
add_hwservice(hal_deviceB_server, hal_deviceB_hwservice)
allow hal_deviceB_client hal_deviceB_hwservice:hwservice_manager find;
# deviceC
binder_call(hal_deviceC_client, hal_deviceC_server)
binder_call(hal_deviceC_server, hal_deviceC_client)
add_hwservice(hal_deviceC_server, hal_deviceC_hwservice)
allow hal_deviceC_client hal_deviceC_hwservice:hwservice_manager find;
在/system/sepolicy/public/hwservice.te添加如下
type hal_deviceA_hwservice, hwservice_manager_type;
type hal_deviceB_hwservice, hwservice_manager_type;
type hal_deviceC_hwservice, hwservice_manager_type;
在system/sepolicy/public/attributes添加如下
hal_attribute(deviceA);
hal_attribute(deviceB);
hal_attribute(deviceC);
/*
#####################################
# hal_attribute(hal_name)
# Add an attribute for hal implementations along with necessary
# restrictions.
define(`hal_attribute', `
attribute hal_$1;
expandattribute hal_$1 true;
attribute hal_$1_client;
expandattribute hal_$1_client true;
attribute hal_$1_server;
expandattribute hal_$1_server false;
neverallow { hal_$1_server -halserverdomain } domain:process fork;
# hal_*_client and halclientdomain attributes are always expanded for
# performance reasons. Neverallow rules targeting expanded attributes can not be
# verified by CTS since these attributes are already expanded by that time.
build_test_only(`
neverallow { hal_$1_server -hal_$1 } domain:process fork;
neverallow { hal_$1_client -halclientdomain } domain:process fork;
')
')
*/
在sepolicy/prebuilts/api/30.0/public/attributes、sepolicy/prebuilts/api/30.0/public/hal_devicesServer.te、sepolicy/prebuilts/api/30.0/public/hwservice.te中的修改同上方一致。
1-3 system/sepolicy/private/和/system/sepolicy/prebuilts目录
在system/sepolicy/private/hwservice_contexts做如下添加
/*
因为 demoComponent HAL 依赖 hwbinder 进行通信,
所以我们还需要编写 hwservice_contexts。
这个文件名也是由 SELinux 框架固定的。
我们在这个文件中将新增的接口定义为一个安全对象,作用和 file_contexts 类似。
*/
vendor.mediatek.hardware.devicesServer::IDeviceAOfHidl u:object_r:hal_deviceA_hwservice:s0
vendor.mediatek.hardware.devicesServer::IDeviceBOfHidl u:object_r:hal_deviceB_hwservice:s0
vendor.mediatek.hardware.devicesServer::IDeviceCOfHidl u:object_r:hal_deviceC_hwservice:s0
在system/sepolicy/prebuilts/api/30.0/private/compat/26.0/26.0.ignore.cil和
system/sepolicy/prebuilts/api/30.0/private/compat/27.0/27.0.ignore.cil和
system/sepolicy/prebuilts/api/30.0/private/compat/28.0/28.0.ignore.cil和
system/sepolicy/prebuilts/api/30.0/private/compat/29.0/29.0.ignore.cil做如下添加
hal_deviceA_hwservice
hal_deviceB_hwservice
hal_deviceC_hwservice
在system/sepolicy/prebuilts/api/30.0/private/hwservice_contexts和
sepolicy/prebuilts/api/30.0/private/compat/26.0/26.0.ignore.cil、
sepolicy/prebuilts/api/30.0/private/compat/27.0/27.0.ignore.cil、
sepolicy/prebuilts/api/30.0/private/compat/28.0/28.0.ignore.cil、
sepolicy/prebuilts/api/30.0/private/compat/29.0/29.0.ignore.cil中的修改同上方一致。
二、其他的添加
兼容性矩阵的添加
hardware/interfaces/compatibility_matrices/compatibility_matrix.5.xml
<hal format="hidl" optional="true">
<name>vendor.mediatek.hardware.devicesServer</name>
<version>1.0</version>
<interface>
<name>IDeviceAOfHidl</name>
<instance>default</instance>
</interface>
<interface>
<name>IDeviceBOfHidl</name>
<instance>default</instance>
</interface>
<interface>
<name>IDeviceCOfHidl</name>
<instance>default</instance>
</interface>
</hal>
device/mediatek/mtxxxx/manifest.xml的添加
<hal format="hidl">
<name>vendor.mediatek.hardware.devicesServer</name>
<transport>hwbinder</transport>
<version>1.0</version>
<interface>
<name>IDeviceAOfHidl</name>
<instance>default</instance>
</interface>
<interface>
<name>IDeviceBOfHidl</name>
<instance>default</instance>
</interface>
<interface>
<name>IDeviceCOfHidl</name>
<instance>default</instance>
</interface>
</hal>
device/mediatek/mtxxxx/
三、创建HIDL接口及对应的服务
这里只创建deviceA作为示例,其余实例也相似,具体看自己需求
3-1 HAL接口的创建
路径:vendor/mediatek/proprietary/hardware/interface/devicesServer/1.0下创建
IDeviceAOfHidl.hal
package vendor.mediatek.hardware.devicesServer@1.0;
import vendor.mediatek.hardware.devicesServer@1.0::IDeviceADataCallback;
interface IDeviceAOfHidl{
init();
release();
sendCommand(vec<uint8_t> data);
setDataCallback(IDeviceADataCallback callback);
};
IDeviceADataCallback.hal
package vendor.mediatek.hardware.devicesServer@1.0;
interface IDeviceADataCallback{
onDataReceived(vec<uint8_t> data);
};
Android.bp
// This file is autogenerated by hidl-gen -Landroidbp.
hidl_interface {
name: "vendor.mediatek.hardware.devicesServer@1.0",
root: "vendor.mediatek.hardware",
srcs: [
"IDeviceADataCallback.hal",
"IDeviceAOfHidl.hal",
"IDeviceBDataCallback.hal",
"IDeviceBOfHidl.hal",
"IDeviceCDataCallback.hal",
"IDeviceCOfHidl.hal",
],
interfaces: [
"android.hidl.base@1.0",
],
gen_java: true,
}
subdirs = [
"default",
]
3-2 service.cpp、rc文件、DeviceAOfHidl.cpp以及Andorid.bp的创建
路径:vendor/mediatek/proprietary/hardware/interface/devicesServer/1.0/default下创建
vendor.mediatek.hardware.devicesServer@1.0-service.rc
service devicesServer-hal-1-0 /vendor/bin/hw/vendor.mediatek.hardware.devicesServer@1.0-service
class hal
user root
group system
service.cpp
#define TAG "devicesServer-1.0-service"
#include <android/log.h>
#include <hidl/HidlTransportSupport.h>
#include <vendor/mediatek/hardware/devicesServer/1.0/IDeviceAOfHidl.h>
#include <vendor/mediatek/hardware/devicesServer/1.0/IDeviceBOfHidl.h>
#include <vendor/mediatek/hardware/devicesServer/1.0/IDeviceCOfHidl.h>
#include <hidl/LegacySupport.h>
#include "DeviceAOfHidl.h"
#include "DeviceBOfHidl.h"
#include "DeviceCOfHidl.h"
using android::sp;
using android::status_t;
using android::OK;
using android::hardware::configureRpcThreadpool;
using android::hardware::joinRpcThreadpool;
using android::hardware::defaultPassthroughServiceImplementation;
using vendor::mediatek::hardware::devicesServer::V1_0::IDeviceAOfHidl;
using vendor::mediatek::hardware::devicesServer::implementation::DeviceAOfHidl;
using vendor::mediatek::hardware::devicesServer::V1_0::IDeviceBOfHidl;
using vendor::mediatek::hardware::devicesServer::implementation::DeviceBOfHidl;
using vendor::mediatek::hardware::devicesServer::V1_0::IDeviceCOfHidl;
using vendor::mediatek::hardware::devicesServer::implementation::DeviceCOfHidl;
int main()
{
android::sp<IDeviceAOfHidl> deviceA_service = new DeviceAOfHidl();
android::sp<IDeviceBOfHidl> deviceB_service = new DeviceBOfHidl();
android::sp<IDeviceCOfHidl> deviceC_service = new DeviceCOfHidl();
configureRpcThreadpool(32, true /*callerWillJoin*/);
status_t status = deviceA_service->registerAsService();
if (status == OK){
deviceA_service->init();
}
status = deviceB_service->registerAsService();
if (status == OK){
deviceB_service->init();
}
status = deviceC_service->registerAsService();
if (status == OK){
deviceC_service->init();
}
joinRpcThreadpool();
LOGD("Cannot register CascadeOfHidlhidl HAL service");
return 1;
}
DeviceAOfHidl.h
// FIXME: your file license if you have one
#pragma once
#include <vendor/mediatek/hardware/devicesServer/1.0/IDeviceAOfHidl.h>
#include <hidl/MQDescriptor.h>
#include <hidl/Status.h>
#include <android/log.h>
#include <thread>
#include <deque>
#include <sys/epoll.h>
#include <unistd.h>
#include <sys/types.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <math.h>
#include <threads.h>
#include <condition_variable>
#include <mutex>
#include <cstring>
#include <termios.h>
namespace vendor::mediatek::hardware::devicesServer::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;
#define LOGV(...) __android_log_print(ANDROID_LOG_VERBOSE, TAG, __VA_ARGS__)
#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG , TAG, __VA_ARGS__)
#define LOGI(...) __android_log_print(ANDROID_LOG_INFO , TAG, __VA_ARGS__)
#define LOGW(...) __android_log_print(ANDROID_LOG_WARN , TAG, __VA_ARGS__)
#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR , TAG, __VA_ARGS__)
struct DeviceAOfHidl : public V1_0::IDeviceAOfHidl {
Return<void> init() override;
Return<void> release() override;
Return<void> sendCommand(const hidl_vec<uint8_t>& data) override;
Return<void> setDataCallback(const sp<::vendor::mediatek::hardware::devicesServer::V1_0::IDeviceADataCallback>& callback) override;
private:
sp<V1_0::IDeviceADataCallback> mCallback = nullptr;
//指令队列
std::deque<char*> deqIn;
//DeviceA线程锁+条件变量
std::condition_variable cv;
std::mutex mtx;
//DeviceA打开的读写文件buffer
int fd_tty;
//串口初始化方法
int init_tty(int fd_tty);
void covertArray2Vector(const char *in, int len, std::vector<uint8_t> &out);
void covertVector2Array(std::vector<uint8_t> in, char *out);
//设置数据回调
int DataReturn(int channel, unsigned char returnData[128], int returnDataLen);
public:
//DeviceA的读写线程
int WriteDataThread();
int ReadDataThread();
};
}
DeviceAOfHidl.cpp
// FIXME: your file license if you have one
#define TAG "devicesServer-1.0-service"
#include "DeviceAOfHidl.h"
namespace vendor::mediatek::hardware::devicesServer::implementation {
Return<void> DeviceAOfHidl::init() {
// TODO implement
std::thread th_read(&DeviceAOfHidl::ReadDataThread, this);
std::thread th_write(&DeviceAOfHidl::WriteDataThread, this);
if(th_read.joinable()){
//这里使用线程分离,不阻塞,避免在service.cpp中出现阻塞,不能注册下一个硬件接口的服务
th_read.detach();
}
if(th_write.joinable()){
th_write.detach();
}
return Void();
}
int DeviceAOfHidl::ReadDataThread()
{
unsigned char tty_ret_val_head[3];
unsigned char tty_ret_val_data[128];
unsigned char ret_val[128];
int ret = 0;
fd_tty = open("/dev/tty0", O_RDWR);
if (fd_tty < 0){
LOGE("open /dev/tty0 failed!\n");
}
if (init_tty(fd_tty) == -1) {
LOGE("init_tty in failed!\n");
}
while(1){
ret = read(fd_tty, &tty_ret_val_head, sizeof(tty_ret_val_head));
DataReturn(0, tty_ret_val_head, 3);
}
return 0;
}
int DeviceAOfHidl::WriteDataThread()
{
unsigned char tty_val_cmd[128];
int cmd_len = 0;
while(1){
std::unique_lock <std::mutex> lck(mtx);
if(deqIn.empty()){
cv.wait(lck);
}else{
char *p = deqIn.front();
memset(tty_val_cmd, 0x00, sizeof(tty_val_cmd));
memcpy(tty_val_cmd, p, 3);
tcflush(fd_tty, TCIFLUSH); //清空输入缓冲区
tcflush(fd_tty, TCOFLUSH); //清空输入缓冲区
int byte_count = write(fd_tty, tty_val_cmd, 3);
deqIn.pop_front();
}
}
return 0;
}
int DeviceAOfHidl::DataReturn(int channel, unsigned char returnData[128], int returnDataLen)
{
if(mCallback != nullptr){
std::vector<uint8_t> out;
char in1[returnDataLen];
for(int i = 0; i < returnDataLen; i++){
in1[i] = returnData[i];
}
covertArray2Vector(in1, returnDataLen, out);
if(channel == 1){}
mCallback->onDataReceived(out);
}
return 0;
}
/*设置串口参数*/
int DeviceAOfHidl::init_tty(int fd_tty)
{
struct termios termios_rfid;
// bzero(&termios_rfid, sizeof(termios_rfid));//清空结构体
memset(&termios_rfid, 0x00, sizeof(termios_rfid));
cfmakeraw(&termios_rfid); //设置终端属性,激活选项
cfsetispeed(&termios_rfid, B115200); //输入波特率
cfsetospeed(&termios_rfid, B115200); //输出波特率
termios_rfid.c_cflag |= CLOCAL | CREAD; //本地连接和接收使能
termios_rfid.c_cflag &= ~CSIZE; //清空数据位
termios_rfid.c_cflag |= CS8; //数据位为8位
termios_rfid.c_cflag &= ~PARENB; //无奇偶校验
termios_rfid.c_cflag &= ~CSTOPB; //一位停止位
tcflush(fd_tty, TCIFLUSH);
termios_rfid.c_cc[VTIME] = 10; //设置等待时间
termios_rfid.c_cc[VMIN] = 1;
tcflush(fd_tty, TCIFLUSH); //清空输入缓冲区
if (tcsetattr(fd_tty, TCSANOW, &termios_rfid)) //激活串口设置
return 0;
return 1;
}
Return<void> DeviceAOfHidl::release() {
// TODO implement
return Void();
}
void DeviceAOfHidl::covertVector2Array(std::vector<uint8_t> in, char *out)
{
int size = in.size();
for (int i = 0; i < size; i++)
{
out[i] = in.at(i);
}
}
void DeviceAOfHidl::covertArray2Vector(const char *in, int len, std::vector<uint8_t> &out)
{
out.clear();
for (int i = 0; i < len; i++)
{
out.push_back(in[i]);
}
}
Return<void> DeviceAOfHidl::sendCommand(const hidl_vec<uint8_t>& data) {
// TODO implement
char *cstr_data = new char[data.size() + 1];
covertVector2Array(data, cstr_data);
std::unique_lock <std::mutex> lck(mtx);
deqIn.push_back(cstr_data);
cv.notify_all();
return Void();
}
Return<void> DeviceAOfHidl::setDataCallback(const sp<::vendor::mediatek::hardware::devicesServer::V1_0::IDeviceADataCallback>& callback) {
mCallback = callback;
return Void();
}
}
Android.bp
// FIXME: your file license if you have one
cc_binary {
name: "vendor.mediatek.hardware.devicesServer@1.0-service",
relative_install_path: "hw",
proprietary: true,
vendor: true,
defaults: ["hidl_defaults"],
init_rc: ["vendor.mediatek.hardware.devicesServer@1.0-service.rc"],
srcs: [
"DeviceAOfHidl.cpp",
"DeviceBOfHidl.cpp",
"DeviceCOfHidl.cpp",
"service.cpp"
],
shared_libs: [
"liblog",
"libcutils",
"libdl",
"libbase",
"libutils",
"libhardware",
"libhidlbase",
"vendor.mediatek.hardware.devicesServer@1.0",
],
}
总结
之前一般是一个硬件设备对应一个模块,在这里是针对一些执行功能比较单一,业务逻辑不复杂的硬件设备进行的分类,多个硬件设备集中在一个模块中,在上层app中使用多个接口进行通信。