目录
概述
HIDL是Hardware Interface Definition Language的简称。在Android Project Treble被提出,在android O中被全面的推送,设计 HIDL 这个机制的目的,主要目的是把框架(framework)与 HAL 进行隔离,使得框架部分可以直接被覆盖、更新,而不需要重新对 HAL 进行编译。
HIDL 实际上是用于进行进程间通信(Inter-process Communication,IPC)的。进程间的通信可以称为 Binder 化(Binderized)。对于必须连接到进程的库,也可以使用 passthough 模式(但在Java中不支持)。
下面编写HIDL实例
实现过程
一、hardware部分
1.1 编写hal
.hal文件的语言格式是C++和Java的结合体。
在 AOSP代码目录创建 hardware/interfaces/serialport/1.0/(1.0代表版本,后续版本升级添加文件2.0)
新建ISerialPort.hal,主要类
package android.hardware.serialport@1.0;
import ISerialPortCallback;
interface ISerialPort {
initSerialPort(string address, int32_t baudrate, int32_t flags);//初始化串口
startRead();//开始读取
stopRead();//结束读取
closeSerialPort();//关闭串口
sendSerialPort(vec<int32_t> buf, int32_t size) generates (bool res);//写入数据
setCallback(int32_t type, ISerialPortCallback callback);//回调读取的数据
};
新建 types.hal (非必要,用于定义结构体,复杂变量可在此定义)
package android.hardware.serialport@1.0;
struct SerialPortEvent {
int32_t what;//数据类型
int32_t length;//数据长度
vec<int32_t> data;//数据
};
enum SerialPortType : int32_t {
VARIOUS = 0,
VDR = 1,
CAR_BODY = 2,
AIR = 3,
KEY = 4,
FCT = 5,
AUTOPILOT = 6,
};
新建ISerialPortCallback.hal,用于回调
package android.hardware.serialport@1.0;
interface ISerialPortCallback {
oneway onDataChange(SerialPortEvent event);
};
1.2 使用hidl-gen生成变量
使用hidl-gen的前提是AOSP全编通过,如果之前全编通过可不用再次全编
source ./build/envsetup.sh
lunch car_mt2712-eng
make -j4
make hidl-gen -j4
设置临时变量
PACKAGE=android.hardware.serialport@1.0
LOC=hardware/interfaces/serialport/1.0/default
使用hidl-gen生成default目录 里的C++文件
hidl-gen -o $LOC -Lc++-impl -randroid.hardware:hardware/interfaces -randroid.hidl:system/libhidl/transport $PACKAGE
使用hidl-gen生成default目录 里的Android.bp文件
hidl-gen -o $LOC -Landroidbp-impl -randroid.hardware:hardware/interfaces -randroid.hidl:system/libhidl/transport $PACKAGE
使用update-makefiles.sh生成1.0目录下的Android.bp
./hardware/interfaces/update-makefiles.sh
此时的目录结构为
└── 1.0
├── Android.bp
├── default
│ ├── Android.bp
│ ├── SerialPortCallback.cpp
│ ├── SerialPortCallback.h
│ ├── SerialPort.cpp
│ ├── SerialPort.h
├── ISerialPortCallback.hal
├── ISerialPort.hal
└── types.hal
1.3 实现.cpp
SerialPort.h (由hidl-gen工具生成)
#ifndef ANDROID_HARDWARE_SERIALPORT_V1_0_SERIALPORT_H
#define ANDROID_HARDWARE_SERIALPORT_V1_0_SERIALPORT_H
#include <android/hardware/serialport/1.0/ISerialPort.h>
#include <hidl/MQDescriptor.h>
#include <hidl/Status.h>
#include "android/log.h"
static const char *TAG="serialport";
#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__)
namespace android {
namespace hardware {
namespace serialport {
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;
struct SerialPort : public ISerialPort {
// Methods from ::android::hardware::serialport::V1_0::ISerialPort follow.
Return<void> initSerialPort(const hidl_string& address, int32_t baudrate, int32_t flags) override;
Return<void> startRead() override;
Return<void> stopRead() override;
Return<void> closeSerialPort() override;
Return<bool> sendSerialPort(const hidl_vec<int32_t>& buf, int32_t size) override;
Return<void> setCallback(int32_t type, const sp<::android::hardware::serialport::V1_0::ISerialPortCallback>& callback) override;
// Methods from ::android::hidl::base::V1_0::IBase follow.
private:
void run();
};
// FIXME: most likely delete, this is only for passthrough implementations
// extern "C" ISerialPort* HIDL_FETCH_ISerialPort(const char* name);
} // namespace implementation
} // namespace V1_0
} // namespace serialport
} // namespace hardware
} // namespace android
#endif // ANDROID_HARDWARE_SERIALPORT_V1_0_SERIALPORT_H
SerialPort.cpp (由hidl-gen工具生成)
实现各个方法,由于代码比较多多,就不贴出来。SerialPort.cpp
1.4 添加启动service
新建android.hardware.serialport@1.0-service.rc 启动脚本
service serialport-hal-1-0 /vendor/bin/hw/android.hardware.serialport@1.0-service
class hal
user system
group system
新建service.cpp
#define LOG_TAG "serialport1.0-service"
#include <android/log.h>
#include <hidl/HidlTransportSupport.h>
#include "SerialPort.h"
using android::sp;
using android::status_t;
using android::OK;
using android::hardware::configureRpcThreadpool;
using android::hardware::joinRpcThreadpool;
using android::hardware::serialport::V1_0::ISerialPort;
using android::hardware::serialport::V1_0::implementation::SerialPort;
int main(int /* argc */, char* /* argv */ []) {
android::sp<ISerialPort> service = new SerialPort();
configureRpcThreadpool(4, true /*callerWillJoin*/);
status_t status = service->registerAsService();
if (status == OK) {
LOGD("SerialPort HAL Ready.");
service->initSerialPort("/dev/ttyS2", 115200, 0);
service->startRead();
joinRpcThreadpool();
}
LOGD("Cannot register Serialport HAL service");
return 1;
}
修改Android.bp
cc_library_shared {
// FIXME: this should only be -impl for a passthrough hal.
// In most cases, to convert this to a binderized implementation, you should:
// - change '-impl' to '-service' here and make it a cc_binary instead of a
// cc_library_shared.
// - add a *.rc file for this module.
// - delete HIDL_FETCH_I* functions.
// - call configureRpcThreadpool and registerAsService on the instance.
// You may also want to append '-impl/-service' with a specific identifier like
// '-vendor' or '-<hardware identifier>' etc to distinguish it.
name: "android.hardware.serialport@1.0-impl",
relative_install_path: "hw",
// FIXME: this should be 'vendor: true' for modules that will eventually be
// on AOSP.
proprietary: true,
srcs: [
"SerialPort.cpp",
],
shared_libs: [
"libhidlbase",
"libhidltransport",
"libutils",
"liblog",
"android.hardware.serialport@1.0",
],
}
cc_binary {
name: "android.hardware.serialport@1.0-service",
relative_install_path: "hw",
defaults: ["hidl_defaults"],
proprietary: true,
init_rc: ["android.hardware.serialport@1.0-service.rc"],
srcs: [
"SerialPort.cpp",
"service.cpp",
],
shared_libs: [
"libbase",
"liblog",
"libdl",
"libutils",
"libhardware",
"libhidlbase",
"libhidltransport",
"android.hardware.serialport@1.0",
],
}
调用 update-makefiles.sh更新一下
1.5 VNDK相关
在目录aosp\build\make\target\product\vndk 里
28.txt 和 current.txt 按照字母顺序新增
VNDK-core: android.hardware.serialport@1.0.so
二、device部分
lunch的是car_mt2712-eng,芯片是MTK的2712,所以在device找到下面目录aosp\device\mediatek/mt2712/manifest_ab.xml,由于是mtk提供的代码,所以在manifest_ab.xml添加,正常应该在manifest.xml
<hal format="hidl">
<name>android.hardware.serialport</name>
<transport>hwbinder</transport>
<version>1.0</version>
<interface>
<name>ISerialPort</name>
<instance>default</instance>
</interface>
</hal>
mediatek/mt2712/device.mk添加 启动 serialport service
# serialport HAL
PRODUCT_PACKAGES += \
android.hardware.serialport@1.0-service \
android.hardware.serialport@1.0-impl
三、SELinux部分——hal service
对应sepolicy, Google 也设定了不同的存放目录, 以便进行分离, 以Google 默认的sepolicy 为例. /system/sepolicy
public: android 和 vendor 共享的sepolicy 定义, 通常情况下, 意味着vendor 开发者可能为此新增一些权限. 一般system/vendor 共用的一些类型和属性的定义, neverallow 限制等会存放于此.
private: 通常意义上的仅限于system image 内部的使用, 不对vendor 开放. 这个只会编译到system image 中.
vendor: 它仅仅能引用public 目录下的相关定义, 这个只会编译到vendor image 中. 但它依旧可以对system image 里面的module 设定sepolicy(对应module 需要在public 下进行声明); 在很大程度上绕过了Google GTS 约束测试.
mapping: 为兼容老版本的sepolicy 而导入, 只有在system image version > vendor version 的时候, 才可能被用到. 即包括两方面, 新版本增加的type , 新版本移除的type, 以及老版本public, 新版本private 等变化的设定, 以兼容老版本.
3.1 vendor 目录
file_contexts 添加
/(vendor|system/vendor)/bin/hw/android\.hardware\.serialport@1\.0-service u:object_r:hal_serialport_default_exec:s0
#file_contexts文件保存系统中所有文件的安全上下文定义,每行前半部分是文件的路径,后面是它的安全上下文的定义(hal_serialport_default_exec)
新建 hal_serialport_default.te
在TE中,所有的东西都被抽象成类型。进程,抽象成类型;资源,抽象成类型。属性,是类型的集合。所以,TE规则中的最小单位就是类型。
#定义一个 名字为 hal_serialport_default 的type
#TYPE是定义主体和客体所属的类型,对于进程而言,它的类型也称为domian。
#通常主体的type具有domian属性,因此,我们也把主体的type称为domain,将domain设置为hal_test_default的属性,表明zygote是用来描述进程的安全上下文的。
type hal_serialport_default, domain;
hal_server_domain(hal_serialport_default, hal_serialport)
type hal_serialport_default_exec, exec_type, vendor_file_type, file_type;
init_daemon_domain(hal_serialport_default)
allow hal_serialport_default serial_device:chr_file rw_file_perms;
3.2 public 目录
attributes 添加
hal_attribute(serialport);
hwservice.te 添加
type hal_serialport_hwservice, hwservice_manager_type;
新建 hal_serialport.te
# HwBinder IPC from client to server, and callbacks
binder_call(hal_serialport_client, hal_serialport_server)
binder_call(hal_serialport_server, hal_serialport_client)
add_hwservice(hal_serialport_server, hal_serialport_hwservice)
allow hal_serialport_client hal_serialport_hwservice:hwservice_manager find;
将以上修改同步到aosp\system\sepolicy\prebuilts\api\28.0\public
3.3 private 目录
hwservice_contexts 添加
android.hardware.serialport::ISerialPort u:object_r:hal_serialport_hwservice:s0
private/compat/26.0/26.0.ignore.cil 添加
hal_serialport_hwservice
private/compat/27.0/27.0.ignore.cil 添加
hal_serialport_hwservice
将以上修改同步到aosp\system\sepolicy\prebuilts\api\28.0\private
四、framework 实现
添加frameworks/baseservices/core/java/com/android/server/SerialService.java,具体添加方法这里就不写了,framework添加新service的方法可以自行百度。下面是主要类
ISerialPortService.aidl
package android.os.serialport;
import android.os.serialport.ISerialPortListener;
interface ISerialPortService {
boolean sendData(in byte [] buf, int size);
void registerCallback(ISerialPortListener listener);
void unRegisterCallback(ISerialPortListener listener);
}
SerialPortService.java
package com.android.server;
import android.hardware.serialport.V1_0.ISerialPort;
import android.hardware.serialport.V1_0.ISerialPortCallback;
import android.hardware.serialport.V1_0.SerialPortEvent;
public class SerialPortService extends ISerialPortService.Stub {
private Context mContext;
private ISerialPort mHalService;
public SerialPortService(Context context) {
mContext = context;
mHalService = ISerialPort.getService();//获取service
if (mHalService != null) {//设置监听回调
mHalService.setCallback(mType, new ISerialPortCallback.Stub() {
@Override
public void onDataChange(SerialPortEvent event) {
}
});
}
}
}
SerialPortManager.java,主要为应用层提供接口
public class SerialPortManager {
public static final String TAG = "SerialPortManager";
private ISerialPortService mService;
private int mMsgId;
public SerialPortManager(ISerialPortService service) {
mService = service;
}
public boolean sendData(byte[] buf, int size) {
try {
if (mService != null) {
ArrayList<Integer> list = new ArrayList();
for (byte b : buf) {
list.add(b & 0xFF);
}
return mService.sendData(buf, size);
}
} catch (RemoteException e) {
e.printStackTrace();
}
return false;
}
public void registerCallback(SerialPortListener listener) {
try {
if (mService != null) {
mService.registerCallback(listener);
}
} catch (Exception e) {
e.printStackTrace();
}
}
public void unRegisterCallback(SerialPortListener listener) {
try {
if (mService != null) {
mService.unRegisterCallback(listener);
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
五、应用层调用
同framework的SerialServiceManager.java提供接口来实现串口通信,sendData用于写数据,callback回调用于读取串口的数据
至此一个完整HIDL实例,用于串口通信,基于android 9.0