简介
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架构如下,我们仿照他来创建一个就好,因为是第一版,文件夹就命名为1.0就好:
系统自带的:
1.创建目录:/hardware/interfaces/fingerprint/1.0,在1.0的文件夹里面添加hal文件,这里需要写三个.hal文件,types.hal、IFingerprint.hal和IFingerprintCallback.hal。types.hal定义的是一些数据结构,IFingerprint.hal定义的是从Framework往HAL调用的接口,而IFingerprintCallback.hal则是HAL往Framework回调的接口。
/*types.hal*/
package [email protected];
struct FingerprintValue{
int32_t propId;
vec values; /*定义一个包含int32_t类型的可变大小的buffer*/
};
这里定义了一个数据类型用于存储指纹数据。
package [email protected];
import IFingerprintCallback;
/*
* 函数声明格式:函数名(参数类型 参数名) generates (返回值类型 返回值名称)
*/
interface IFingerprint {
subscribe(IFingerprintCallback callback) generates (bool retval);
unsubscribe() generates (bool retval);
set(FingerprintValue fingerprintvalue) generates (bool retval);
};
这里定义来了三个方法,供framework层调用,分别是:订阅IFingerprintCallback回调、取消订阅、设置数据到驱动。
package [email protected];
interface IFingerprintCallback {
oneway onFingerprintEvent(FingerprintValue fingerprintvalue);
};
这里就是刚才订阅的回调方法,用于回调数据给framework。
2.写完这三个hal文件后,在源码根目录下导环境变量,然后进入到/hardware/interfaces目录执行如下命令:
./update-makefiles.sh,这样就会生成一些Anddroid.bp文件、Android.mk文件和一些必要的东西。
可能会有如下两种错误:
错误1:提示update-makefiles-help.sh找不到
原因:环境变量ANDROID_BUILD_TOP没有指向当前项目目录
解决方法:在当前终端执行source build/envsetup.sh和lunch命令后再执行。
错误2:提示hidl-gen命令找不到
原因:项目没有经过编译,没有产生hidl-gen工具
解决方法:执行make hidl-gen,编译成功后hidl-gen的生成路径为:out/host/linux-x86/bin/hidl-gen
当正确执行来./update-makefiles.sh后,目录下的结构变成如下:
在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或Android.mk用于编译。
# [email protected]
service fingerprint-hal-1-0 /vendor/bin/hw/[email protected]
# "class hal" causes a race condition on some devices due to files created
# in /data. As a workaround, postpone startup until later in boot once
# /data is mounted.
class hal
user system
group system
/*service.cpp*/
#define LOG_TAG "[email protected]"
#include
#include
#include
#include
#include
#include "Fingerprint.h"
using android::hardware::fingerprint::V1_0::IFingerprint;
using android::hardware::fingerprint::V1_0::implementation::Fingerprint;
using android::hardware::configureRpcThreadpool;
using android::hardware::joinRpcThreadpool;
using android::sp;
int main(){
configureRpcThreadpool(4, true /*callerWillJoin*/);
Fingerprint fingerprint;
auto status = fingerprint.registerAsService();
CHECK_EQ(status, android::OK) << "Failed to register fingerprint HAL impletion";
joinRpcThreadpool();
return 0; // should never get here
}
这是绑定式HAL的service.cpp写法。
Android.mk写法:
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := [email protected]
LOCAL_INIT_RC := [email protected]
LOCAL_PROPRIETARY_MODULE := true
LOCAL_MODULE_RELATIVE_PATH := hw
LOCAL_SRC_FILES := \
Fingerprint.cpp \
service.cpp \
LOCAL_SHARED_LIBRARIES := \
libcutils \
liblog \
libbase \
libhidlbase \
libhidltransport \
libhardware \
libutils \
[email protected] \
include $(BUILD_EXECUTABLE)
Android.bp写法:
//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能使用。
1.修改系统配置
编写完上面的文件,如果直接调用接口,会出现如下错误提示:
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/mediatek/mt6739/manifest.xml
android.hardware.fingerprint
hwbinder
1.0
IFingerprint
default
另外需要在android/device/mediatek/mt6739/device.mk文件增加[email protected]这样才会将[email protected]编译进系统,开机才能启动:
一般高通会有点不一样,在android/device/qcom/msm8996/msm8996.mk类似这样的地方,不同厂商命名会不同,但是修改还是一样的
除了上面的错误,我们可能还会如下的报错:
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/xx平台/sepolicy/xx芯片/file_contexts,增加如下代码:
/(vendor|system/vendor)/bin/hw/[email protected] u:object_r:hal_luofingerprint_default_exec:s0
上面的hal_luofingerprint_default是我们自己定义的,为什么要加个luo呢?因为如果不加就跟原生的冲突了。。
还需要在android/device/xx平台/sepolicy/xx芯片/下增加hal_luofingerprint_default.te文件,代码如下:
//hal_luofingerprint_default.te
type hal_luofingerprint_default, domain; //Set a new domain called hal_luofingerprint_default
type hal_luofingerprint_default_exec, exec_type, vendor_file_type, file_type; //Set your exec file type
init_daemon_domain(hal_luofingerprint_default) //Setup for domain transition
hal_server_domain(hal_luofingerprint_default, hal_fingerprint); //Set your domain as server domain of hal_fingerprint in which define by AOSP already
这样hal的代码就完成啦!
编译上面的hal文件会产生HIDL的文件,所以我们可以直接调用接口,而不用一定要像之前一样写个JNI出来给APP使用。写一个JNI出来也是可以的,如果需要在JNI中加一些简单逻辑可以写个JNI,看需求而定。具体可参考收音机模块,路径android/hardware/interfaces/broadcastradio,JNI在android/frameworks/base/services/core/jni/BroadcastRadio
直接抛代码了。
//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文件的。