本文基于Android8.1源码。
这里介绍的是一种简单HAL的写法与调用。
我将会编写一个app直接调用HAL的接口,而HAL层代码将直接读写驱动的节点。
Android O的一项新元素是 Project Treble。这是 Android 操作系统框架在架构方面的一项重大改变,旨在让制造商以更低的成本更轻松、更快速地将设备更新到新版 Android 系统。
在Android O之前,HAL是一个个的.so库,通过dlopen来进行打开,库和framework位于同一个进程。
新的架构之下,framework和hal运行于不同的进程,所有的HAL采用新的HIDL技术来完成。作为此变化的一部分,运行 Android 8.0 的设备必须支持绑定式或直通式 HAL:
绑定式 HAL。以 HAL 接口定义语言 (HIDL) 表示的 HAL。这些 HAL 取代了早期 Android 版本中使用的传统 HAL 和旧版 HAL。在绑定式 HAL 中,Android 框架和 HAL 之间通过 Binder 进程间通信 (IPC) 调用进行通信。所有在推出时即搭载了 Android 8.0 或后续版本的设备都必须只支持绑定式 HAL。
直通式 HAL。以 HIDL 封装的传统 HAL 或旧版 HAL。这些 HAL 封装了现有的 HAL,可在绑定模式和
Same-Process(直通)模式下使用。升级到 Android 8.0 的设备可以使用直通式 HAL。
一看就知道绑定式HAL是主推方法了。后面的HAL也将用绑定式HAL方式编写。
源码里面有一个指纹识别,路径在/android/hardware/interfaces/biometrics/fingerprint。
这里将编写一个简单版的fingerprint HAL。只有set、subscribe、unsubscribe和callback回调方法。直接往驱动节点里面读写数据,操作驱动节点的方法主要有三个open、write、read。
我们需要写三个.hal文件,types.hal、IFingerprint.hal和IFingerprintCallback.hal。
types.hal定义的是一些数据结构,IFingerprint.hal定义的是从Framework往HAL调用的接口,而IFingerprintCallback.hal则是HAL往Framework回调的接口。
首先在/hardware/interfaces目录下新建一个fingerprint文件夹,然后在fingerprint里面再新建一个名叫1.0的文件夹,在1.0的文件夹里面添加hal文件。
//types.hal
package [email protected];
struct FingerprintValue {
/** Property identifier */
int32_t propId;
vec values;
};
这里只定义一个简单的结构体,用于存储指纹数据。
//IFingerprint.hal
package [email protected];
import IFingerprintCallback;
interface IFingerprint {
subscribe(IFingerprintCallback callback) generates (bool retval);
unsubscribe() generates (bool retval);
set(FingerprintValue fingerprintValue) generates (bool retval);
};
这里定义了三个方法,订阅IFingerprintCallback回调、取消订阅、设置数据到驱动。
//IFingerprintCallback.hal
package [email protected];
interface IFingerprintCallback {
oneway onFingerprintEvent(FingerprintValue fingerprintValue);
};
这里就是刚才订阅的回调方法,用于回调数据给应用。
写完这三个hal文件后,在源码根目录下导环境变量,然后进入到/hardware/interfaces目录执行如下命令:
./update-makefiles.sh
这样就会生成一些bp文件、mk文件和一些必要的东西。
在1.0文件夹里面新建一个default文件夹。
编写Fingerprint.h和Fingerprint.cpp,我们将在Fingerprint.cpp里面实现我们刚才在hal文件里面定义的方法,读写驱动节点数据。代码如下:
//Fingerprint.h
#ifndef ANDROID_HARDWARE_FINGERPRINT_V1_0_FINGERPRINT_H
#define ANDROID_HARDWARE_FINGERPRINT_V1_0_FINGERPRINT_H
#include
namespace android {
namespace hardware {
namespace fingerprint {
namespace V1_0 {
namespace implementation {
using ::android::hardware::fingerprint::V1_0::IFingerprint;
using ::android::hardware::fingerprint::V1_0::IFingerprintCallback;
using ::android::hardware::fingerprint::V1_0::FingerprintValue;
struct Fingerprint : public IFingerprint {
Fingerprint();
~Fingerprint();
Return subscribe(const sp& callback) override;
Return unsubscribe() override;
Return set(const FingerprintValue& fingerprintValue) override;
private:
void fingerprint_message_recv_thread_init();
void fingerprint_message_recv_thread_destroy();
void fingerprint_message_recv_thread_subscribe();
void fingerprint_message_recv_thread_unsubscribe();
};
extern "C" IFingerprint* HIDL_FETCH_IFingerprint(const char* name);
} // namespace implementation
} // namespace V1_0
} // namespace fingerprint
} // namespace hardware
} // namespace android
#endif // ANDROID_HARDWARE_FINGERPRINT_V1_0_FINGERPRINT_H
//Fingerprint.cpp
#define LOG_TAG "[email protected]"
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include "Fingerprint.h"
#define REMOTE_MESSAGE_BUFFER_SIZE 36
#define REMOTE_MESSAGE_PRINT_BUFFER_SIZE 256
#define REMOTE_MESSAGE_PRINT_MAX_COUNT 64
namespace android {
namespace hardware {
namespace fingerprint {
namespace V1_0 {
namespace implementation {
struct hw_fingerprint_device_impl {
int fd;
};
struct hw_fingerprint_recv_thread
{
pthread_t t_id;
pthread_attr_t t_attr;
bool t_exitflag;
};
static sp mCallback = 0;
static struct hw_fingerprint_device_impl g_fingerprint_impl =
{
.fd = -1,
};
static struct hw_fingerprint_recv_thread g_fingerprint_recv_thread =
{
.t_id = 0,
.t_exitflag = false,
};
static void* fingerprint_message_recv_thread_proc(void *arg)
{
ALOGD("%s", __FUNCTION__);
unsigned char data[REMOTE_MESSAGE_BUFFER_SIZE];
FingerprintValue fingerprintValue;
while (g_fingerprint_recv_thread.t_exitflag == false)
{
if(g_fingerprint_impl.fd != -1)
{
memset(data, 0, REMOTE_MESSAGE_BUFFER_SIZE);
int len = read(g_fingerprint_impl.fd, data, REMOTE_MESSAGE_BUFFER_SIZE); //从驱动节点读取数据
if (len > 0){
if( data[0] != 0 ){
//report message
fingerprintValue.values.resize(len);
for(int i = 0; i < len ; i++){
fingerprintValue.values[i] = data[i];
}
if(mCallback != 0)
{
mCallback->onFingerprintEvent(fingerprintValue);//回调数据给应用
}else{
ALOGE("Try to mCallback->onFingerprintEvent but mCallback==0!!!");
}
}else{
usleep(100000);
}
}
}
else
{
ALOGE("Device has not been initialized!");
}
}
ALOGD("%s END!", __FUNCTION__);
return NULL;
}
Fingerprint::Fingerprint()
{
ALOGD("%s", __FUNCTION__);
if(-1 == g_fingerprint_impl.fd)
{
g_fingerprint_impl.fd = open("/dev/i2c-6", O_RDWR | O_NOCTTY | O_NDELAY);//打开驱动节点
if(-1 == g_fingerprint_impl.fd)
{
ALOGE("Open device \"/dev/i2c-6\" failed!");
}
}
}
Fingerprint::~Fingerprint() {
if(-1 != g_fingerprint_impl.fd)
{
close(g_fingerprint_impl.fd);
g_fingerprint_impl.fd = -1;
}
}
void Fingerprint::fingerprint_message_recv_thread_subscribe()
{
ALOGD("%s", __FUNCTION__);
g_fingerprint_recv_thread.t_exitflag = false;
if (pthread_create(&g_fingerprint_recv_thread.t_id,
NULL,
fingerprint_message_recv_thread_proc,
NULL))
{
g_fingerprint_recv_thread.t_id = 0;
}
}
void Fingerprint::fingerprint_message_recv_thread_unsubscribe()
{
ALOGD("%s", __FUNCTION__);
if(g_fingerprint_recv_thread.t_id != 0)
{
g_fingerprint_recv_thread.t_exitflag = true;
void* lpv = NULL;
pthread_join(g_fingerprint_recv_thread.t_id, &lpv);
g_fingerprint_recv_thread.t_id = 0;
}
}
//订阅的时候开启一个线程从驱动节点一直read数据
Return Fingerprint::subscribe(const sp& callback) {
ALOGD("%s", __FUNCTION__);
mCallback = callback;
if(-1 != g_fingerprint_impl.fd)
{
fingerprint_message_recv_thread_subscribe();//创建线程read数据
return true;
}
return false;
}
Return Fingerprint::unsubscribe() {
ALOGD("%s", __FUNCTION__);
//signal(SIGIO, SIG_IGN);
if(-1 != g_fingerprint_impl.fd)
{
fingerprint_message_recv_thread_unsubscribe();
}
return true;
}
Return Fingerprint::set(const FingerprintValue& fingerprintValue) {
ALOGD("%s", __FUNCTION__);
std::vector vecData;
unsigned int write_size = 0;
for (auto value : fingerprintValue.values) {
vecData.push_back((uint8_t)value);
ALOGD("%s , value : %d", __FUNCTION__,value);
}
int msg_length = vecData.size();
if(-1 != g_fingerprint_impl.fd){
write_size = write(g_fingerprint_impl.fd, (char *)vecData.data(), msg_length);//往驱动节点写数据
if(msg_length != write_size){
ALOGE("Fingerprint Send message failed! write_size: %d != msg_length: %d !", write_size, msg_length);
return false;
}
}else{
ALOGE("Fingerprint Device has not been initialized!");
return false;
}
return true;
}
IFingerprint* HIDL_FETCH_IFingerprint(const char* /* name */) {
return new Fingerprint();
}
} // namespace implementation
} // namespace V1_0
} // namespace fingerprint
} // namespace hardware
} // namespace android
编写完上面两个文件,再执行一下./update-makefiles.sh更新一下。
之后还有编写[email protected] 启动脚本, service.cpp入口 ,Android.bp用于编译。
//[email protected]
service fingerprint-hal-1-0 /vendor/bin/hw/[email protected]
class hal
user system
group system
//service.cpp
#define LOG_TAG "[email protected]"
#include
#include
#include
#include
#include "Fingerprint.h"
using android::hardware::configureRpcThreadpool;
using android::hardware::joinRpcThreadpool;
using android::hardware::fingerprint::V1_0::implementation::Fingerprint;
int main() {
configureRpcThreadpool(4, true);
Fingerprint fingerprint;
auto status = fingerprint.registerAsService();
CHECK_EQ(status, android::OK) << "Failed to register fingerprint HAL implementation";
joinRpcThreadpool();
return 0; // joinRpcThreadpool shouldn't exit
}
这是绑定式HAL的service.cpp写法。
//Android.bp
cc_binary {
vendor: true,
relative_install_path: "hw",
name: "[email protected]",
init_rc: ["[email protected]"],
srcs: [
"Fingerprint.cpp",
"service.cpp",
],
cflags: [
"-Wall",
],
shared_libs: [
"liblog",
"libutils",
"libhidltransport",
"[email protected]",
"libdl",
"libhardware",
"libhidlbase",
"libbase",
],
}
好了,主要代码算是写完了。
之后还需要修改一下配置文件,使我们写的hal能使用。
#修改系统配置
编写完上面的文件,如果直接调用接口,会出现如下错误提示:
3004.17> 05-09 16:47:40.519 2909 2909 I example.com.fingerprinttest: Looking for service [email protected]::IFingerprint/default
3004.17> 05-09 16:47:40.521 473 473 W /system/bin/hwservicemanager: getTransport: Cannot find entry [email protected]::IFingerprint/default in either framework or device manifest.
3004.17> 05-09 16:47:40.521 701 1474 I AudioPolicyManagerCustom: FLAG None hence request for a primary output
3004.17> 05-09 16:47:40.522 2909 2909 E example.com.fingerprinttest: service [email protected]::IFingerprint declares transport method EMPTY but framework expects hwbinder.
3004.17> 05-09 16:47:40.522 2909 2909 D AndroidRuntime: Shutting down VM
3004.17> 05-09 16:47:40.523 701 1474 I AudioPolicyManagerCustom: FLAG None hence request for a primary output
3004.17> 05-09 16:47:40.523 2909 2909 E AndroidRuntime: FATAL EXCEPTION: main
3004.17> 05-09 16:47:40.523 2909 2909 E AndroidRuntime: Process: uidq0161.example.com.fingerprinttest, PID: 2909
3004.17> 05-09 16:47:40.523 2909 2909 E AndroidRuntime: java.util.NoSuchElementException
3004.18> 05-09 16:47:40.523 2909 2909 E AndroidRuntime: at android.os.HwBinder.getService(Native Method)
3004.18> 05-09 16:47:40.523 2909 2909 E AndroidRuntime: at android.hardware.fingerprint.V1_0.IFingerprint.getService(IFingerprint.java:44)
3004.18> 05-09 16:47:40.523 2909 2909 E AndroidRuntime: at example.com.fingerprinttest.MainActivity.onClick(MainActivity.java:40)
3004.18> 05-09 16:47:40.523 2909 2909 E AndroidRuntime: at android.view.View.performClick(View.java:6294)
3004.18> 05-09 16:47:40.523 2909 2909 E AndroidRuntime: at android.view.View$PerformClick.run(View.java:24774)
这就是在提示我们需要修改manifest.xml文件,增加以下代码才能使应用调用该hal的时候不会出错。
// android/device/qcom/msm8996/manifest.xml
android.hardware.fingerprint
hwbinder
1.0
IFingerprint
default
另外需要在android/device/qcom/msm8996/msm8996.mk文件
增加[email protected]这样开机就会启动[email protected]
msm8996是高通自己修改的文件名,不同厂商命名会不同,但是修改还是一样的。
除了上面的错误,我们可能还会如下的报错:
Line 254: [ 42.716694] init: computing context for service 'fingerprint_hal_service'
Line 255: [ 42.717950] init: service fingerprint_hal_service does not have a SELinux domain defined
这是因为fingerprint_hal_service未启动,原因为SELinux保护机制使得HWService未启动。
因为Google 要求init 启动service 时,都要进行SELinux domain切换,即从init domain切换到另外的domain。这个是从安全方面考虑的,默认init domain 的SELinux 权限很大,可以做很多危险行为,比如mount image, kill process 等等。如果普通service 使用 init domain,就增大了可攻击的安全面。
Google 在CTS 中有对这一项进行检查, CTS fail of android.security.cts.SELinuxDomainTest # testInitDomain
通常情况下,如果init 启动的是一个可快速执行完的oneshot 的service, 即非deamon 程序, “一闪而过” 的话,可以不进行domain 切换. 此类CTS 检测不到. 如果init 启动的是常驻内存的deamon service, 那么一定要进行domain 切换.(L0/L1 版版本)
但在M版本上,Google 增强了这块的约束,通过使用neverallow init { file_type fs_type}:file execure_no_trans;严格限制了init 启动service 都必须进行domain 切换,否则service 无法启动!!!
解决方法如下:
修改 android/device/qcom/sepolicy/msm8996/file_contexts,增加如下代码:
/(vendor|system/vendor)/bin/hw/[email protected] u:object_r:hal_xiaoheifingerprint_default_exec:s0
上面的hal_xiaoheifingerprint_default是我们自己定义的,为什么要加个xiaohei呢?因为如果不加就跟源生的冲突了。。
还需要android/device/qcom/sepolicy/msm8996/下增加hal_xiaoheifingerprint_default.te文件,代码如下:
//hal_xiaoheifingerprint_default.te
type hal_xiaoheifingerprint_default, domain; //Set a new domain called hal_xiaoheifingerprint_default
type hal_xiaoheifingerprint_default_exec, exec_type, vendor_file_type, file_type; //Set your exec file type
init_daemon_domain(hal_xiaoheifingerprint_default) //Setup for domain transition
hal_server_domain(hal_xiaoheifingerprint_default, hal_fingerprint); //Set your domain as server domain of hal_fingerprint in which define by AOSP already
这样hal的代码就完成啦!
编译上面的hal文件会产生HIDL的文件,所以我们可以直接调用接口,而不用一定要像之前一样写个JNI出来给APP使用。写一个JNI出来也是可以的,具体可参考收音机模块,路径android/hardware/interfaces/broadcastradio,JNI在android/frameworks/base/services/core/jni/BroadcastRadio。在Android P中发现收音机模块也是直接去掉了JNI,这样看来Android O的JNI只是一种过渡。
直接抛代码了。
//MainActivity.java
package example.com.fingerprinttest;
import android.hardware.fingerprint.V1_0.FingerprintValue;
import android.hardware.fingerprint.V1_0.IFingerprint;
import android.app.Activity;
import android.os.Bundle;
import android.os.RemoteException;
import android.util.Log;
import android.view.View;
import android.widget.Button;
public class MainActivity extends Activity implements View.OnClickListener {
Button btn_subscribe, btn_unsubscribe, btn_set;
IFingerprint fingerprintService;
FingerprintCallback fingerprintCallback = new FingerprintCallback();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
try {
fingerprintService = IFingerprint.getService();//获取service
} catch (RemoteException e) {
e.printStackTrace();
}
btn_subscribe = (Button) this.findViewById(R.id.btn_subscribe);
btn_unsubscribe = (Button) this.findViewById(R.id.btn_unsubscribe);
btn_set = (Button) this.findViewById(R.id.btn_set);
btn_subscribe.setOnClickListener(this);
btn_unsubscribe.setOnClickListener(this);
btn_set.setOnClickListener(this);
}
@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.btn_subscribe:
try {
fingerprintService.subscribe(fingerprintCallback);//订阅callback
} catch (RemoteException e) {
e.printStackTrace();
}
break;
case R.id.btn_unsubscribe:
try {
fingerprintService.unsubscribe();//取消订阅callback
} catch (RemoteException e) {
e.printStackTrace();
}
break;
case R.id.btn_set:
FingerprintValue fingerprintValue = new FingerprintValue();
fingerprintValue.values.add(1);
fingerprintValue.values.add(3);
try {
fingerprintService.set(fingerprintValue);//发送消息给hal
} catch (RemoteException e) {
e.printStackTrace();
}
break;
}
}
}
//FingerprintCallback.java
package example.com.fingerprinttest;
import android.hardware.fingerprint.V1_0.FingerprintValue;
import android.hardware.fingerprint.V1_0.IFingerprintCallback;
import android.os.RemoteException;
import android.util.Log;
public class FingerprintCallback extends IFingerprintCallback.Stub{
@Override
public void onFingerprintEvent(FingerprintValue fingerprintValue) throws RemoteException {
Log.d("Fingerprint", "FingerprintCallback onFingerprintEvent~~~~~~");
//在这处理回调的逻辑
}
}
//Android.mk
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_PACKAGE_NAME := FingerprintTest
LOCAL_CERTIFICATE := platform
LOCAL_MODULE_TAGS := optional
LOCAL_DEX_PREOPT := false
LOCAL_PROGUARD_ENABLED:= disabled
LOCAL_STATIC_JAVA_LIBRARIES := android.hardware.fingerprint-V1.0-java-static
LOCAL_SRC_FILES := $(call all-subdir-java-files)
include $(BUILD_PACKAGE)
在源码下编译是需要编写一个mk文件的。
fingerprint hal代码下载