怎么实现硬件访问服务
1、JNI和HAL
com_andorid_server_ledService.cpp
hal_led.c
2、修改onload.cpp 调用
com_andorid_server_ledService.cpp 实现的函数
3、修改systemServer.java
new ledService & addService
4、LedService.java 调用本地方法实现硬件操作
5、ILEDServivce.java 给app使用
----------------------------------------------------------------------------------------------------------------------
1、先来实现接口文件 ILEDService.java ,给app提供接口,aidl 文件,安卓接口定义语言
参考 android-5.0.2\frameworks\base\core\java\android\os\
IVibratorService.aidl
ILedService.aidl
package android.os;
/** {@hide} */
interface ILedService
{
int ledCtrl(int which, int status);
}
对于LED来说,提供给应用程序的只有一个控制接口,对于打开和关闭app不需要关心。
修改
android-5.0.2\frameworks\base\andorid.mk 仿照
IVibratorService.aidl
添加 ILedService.aidl
mmm . 编译,mmm 命令无法执行的话先执行 . setenv
ILedService.java 生成在 out 目录
ILedService.java 声明了app能够调用的接口函数,这个接口函数需要在 LedService 类中实现
public int ledCtrl(int which, int status) throws android.os.RemoteException;
2、实现 LedService 类
ILedService.java 提供接口,那么接口函数在哪里实现?在LedService.java
既然是个java文件,那么它必然无法直接调用硬件,还是需要通过JNI来访问硬件,JNI文件在
com_andorid_server_ledService.cpp
参考:vibratorService.java (frameworks\base\services\core\java\com\android\server)
package com.android.server;
import android.os.ILedService;
public class LedService extends ILedService.Stub
private static final String TAG = "LedService";
/* call native c function */
public int native_Ctrl(int which, int status);
public int native_Open();
public void native_Close();
public int ledCtrl(int which, int status)
{
return native_Ctrl(which, status);
}
public LedService(){
native_Open();
}
}
将 LedService.java 拷贝到
frameworks\base\services\core\java\com\android\server
无需 .mk 文件,
frameworks\base\services\core\Android.mk 默认包含了全部 java 文件
如何注册 Service 到 ServerManger ?
参考:S
ystemServer.java (frameworks\base\services\java\com\android\server)
Slog.i(TAG, "Vibrator Service");
vibrator = new VibratorService(context);
ServiceManager.addService("vibrator", vibrator);
Slog.i(TAG, "Led Service");
ServiceManager.addService("led", new LedService());
注册之后,app 就可以使用 ILedService 接口
参考: systemVibrator.java (frameworks\base\core\java\android\os)
public class SystemVibrator extends Vibrator {
private static final String TAG = "Vibrator";
private final IVibratorService mService;
private final Binder mToken = new Binder();
//应用程序中看到 ServiceManager.getService("xxx")就应该知道它使用的是硬件访问服务
public SystemVibrator() {
mService = IVibratorService.Stub.asInterface(
ServiceManager.getService("vibrator"));//向上转型成接口
}
public SystemVibrator(Context context) {
super(context);
mService = IVibratorService.Stub.asInterface(
ServiceManager.getService("vibrator"));
}
public boolean hasVibrator() {
if (mService == null) {
Log.w(TAG, "Failed to vibrate; no vibrator service.");
return false;
}
try {
return mService.hasVibrator();//调用提供的接口函数
} catch (RemoteException e) {
}
return false;
}
...
3、实现 JNI 文件 com_android_server_ledService.cpp
注册本地方法,供 LedService 使用
参考:com_android_server_VibratorService.cpp (frameworks\base\services\core\jni)
#define LOG_TAG "LedService"
#include "jni.h"
#include "JNIHelp.h"
#include "android_runtime/AndroidRuntime.h"
#include <utils/misc.h>
#include <utils/Log.h>
#include <hardware_legacy/vibrator.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <android/log.h> /* liblog */
static jint fd;
jint ledOpen(JNIEnv *env, jobject cls)
{
ALOGI("native ledOpen");
printf("%s\n",__func__);
fd = open("/dev/leds", O_RDWR);
if (fd < 0)
{
ALOGI("native ledOpen error");
}
return fd;
}
void ledClose(JNIEnv *env, jobject cls, jint a, jint b)
{
close(fd);
ALOGI("native ledClose");
}
jint ledCtrl(JNIEnv *env, jobject cls, jint which, jint status)
{
int ret = ioctl(fd, status, which);
ALOGI("native ledCtrl which %d static %d", which, status);
return ret;
}
namespace android
{
static JNINativeMethod method_table[] = {
{ "native_Open", "()I", (void*)ledOpen },
{ "native_Close", "()V", (void*)ledClose },
{ "native_Ctrl", "(II)I", (void*)ledCtrl }
};
int register_android_server_LedService(JNIEnv *env)
{
return jniRegisterNativeMethods(env, "com/android/server/LedService",
method_table, NELEM(method_table));
}
};
修改
Onload.cpp (frameworks\base\services\core\jni)
int register_android_server_LedService(JNIEnv *env);
register_android_server_LedService(env);
将 com_andorid_server_LedService.cpp 拷贝到
frameworks\base\services\core\jni 目录
修改
frameworks\base\services\core\jni\Android.mk 添加
$(LOCAL_REL_DIR)/com_android_server_LedService.cpp \
共计修改了两个 mk 文件
frameworks\base\services\core\Android.mk
frameworks\base\services\core\jni\Android.mk
编译:
mmm
frameworks/base/services
make snod
./gen-img.sh
4、写 app 程序
import android.os.ILedService; //导入接口
private ILedService ledService = null;
ledService = ILedService.Stub.asInterface(ServiceManager.getService("led"));//获得ledService
ledService.ledCtrl(i, 0); //使用ledService
android studio 不能直接使用 ILedService 接口,需要导入 classes.jar
5、JNI 再分层,实现一个 so 文件,供 JNI 调用
JNI 文件和 so 文件均为 C 实现,在分层的好处时,更改硬件操作只需要更改 so 文件,无需重新编译 system,
此外,有一定的保密作用。
那么,JNI 如何调用 so 文件?和 C 语言调用库文件大概是一样的吧,dlopen ,但是安卓肯定会有自己的封装。
分层之后,
JNI 向上提供本地函数,向下加载 hal so 文件
HAL 负责访问硬件驱动程序
安卓中使用如下函数加载 so 文件,
传入一个字符串,获得一个 hw_module_t 结构体
int hw_get_module(const char *id, const struct hw_module_t **module)
{
return hw_get_module_by_class(id, NULL, module);
}
int hw_get_module_by_class(const char *class_id, const char *inst,
const struct hw_module_t **module)
{
/* 查找 so 文件是否存在 */
found:
/* load the module, if this fails, we're doomed, and we should not try
* to load a different variant. */
return load(class_id, path, module);
}
static int load(const char *id,
const char *path,
const struct hw_module_t **pHmi)
{
int status;
void *handle;
struct hw_module_t *hmi;
/*
* load the symbols resolving undefined symbols before
* dlopen returns. Since RTLD_GLOBAL is not or'd in with
* RTLD_NOW the external symbols will not be global
*/
handle = dlopen(path, RTLD_NOW);
if (handle == NULL) {
char const *err_str = dlerror();
ALOGE("load: module=%s\n%s", path, err_str?err_str:"unknown");
status = -EINVAL;
goto done;
}
/* Get the address of the struct hal_module_info. */
const char *sym = HAL_MODULE_INFO_SYM_AS_STR;//"HMI"
/* 根据 动态链接库 操作句柄(handle)与符号(symbol),返回符号对应的地址。使用这个函数不但可以获取函数地址,也可以获取变量地址 ,在此获取名为 "HMI" 的结构体 */
hmi = (struct hw_module_t *)dlsym(handle, sym);
if (hmi == NULL) {
ALOGE("load: couldn't find symbol %s", sym);
status = -EINVAL;
goto done;
}
/* Check that the id matches */
if (strcmp(id, hmi->id) != 0) {
ALOGE("load: id=%s != hmi->id=%s", id, hmi->id);
status = -EINVAL;
goto done;
}
hmi->dso = handle;
/* success */
status = 0;
done:
if (status != 0) {
hmi = NULL;
if (handle != NULL) {
dlclose(handle);
handle = NULL;
}
} else {
ALOGV("loaded HAL id=%s path=%s hmi=%p handle=%p",
id, path, *pHmi, handle);
}
*pHmi = hmi;//hw_module_t
return status;
}
如何使用,参考:com_android_server_lights_LightsService.cpp (frameworks\base\services\core\jni)
libhardware/include/hardware/lights.h:31:#define LIGHTS_HARDWARE_MODULE_ID "lights"
static jlong init_native(JNIEnv *env, jobject clazz)
{
int err;
hw_module_t* module;
Devices* devices;
devices = (Devices*)malloc(sizeof(Devices));
err = hw_get_module(LIGHTS_HARDWARE_MODULE_ID, (hw_module_t const**)&module);
if (err == 0) {
devices->lights[LIGHT_INDEX_BACKLIGHT]
= get_device(module, LIGHT_ID_BACKLIGHT);
devices->lights[LIGHT_INDEX_KEYBOARD]
= get_device(module, LIGHT_ID_KEYBOARD);
...
} else {
memset(devices, 0, sizeof(Devices));
}
return (jlong)devices;
}
static light_device_t* get_device(hw_module_t* module, char const* name)
{
int err;
hw_device_t* device;//这里直接定义成 light_device_t 更好理解
err = module->methods->open(module, name, &device);//hw_module_t -> hw_device_t
if (err == 0) {
return (light_device_t*)device;//把device地址处的东东转换为具体的结构体
} else {
return NULL;
}
}
struct light_device_t {
struct hw_device_t common;
/**
* Set the provided lights to the provided values.
*
* Returns: 0 on succes, error code on failure.
*/
int (*set_light)(struct light_device_t* dev,
struct light_state_t const* state);
};
device 如何使用?
static void setLight_native(JNIEnv *env, jobject clazz, jlong ptr,
jint light, jint colorARGB, jint flashMode, jint onMS, jint offMS, jint brightnessMode)
{
Devices* devices = (Devices*)ptr;
light_state_t state;
if (light < 0 || light >= LIGHT_COUNT || devices->lights[light] == NULL) {
return ;
}
memset(&state, 0, sizeof(light_state_t));
state.color = colorARGB;
state.flashMode = flashMode;
state.flashOnMS = onMS;
state.flashOffMS = offMS;
state.brightnessMode = brightnessMode;
{
ALOGD_IF_SLOW(50, "Excessive delay setting light");
devices->lights[light]->set_light(devices->lights[light], &state);//so 文件中实现
}
}
参考:/hardware/libhardware/modules/vibrator/vibrator.c
typedef struct vibrator_device {
struct hw_device_t common;
int (*vibrator_on)(struct vibrator_device* vibradev, unsigned int timeout_ms);
int (*vibrator_off)(struct vibrator_device* vibradev);
} vibrator_device_t;
#include <hardware/vibrator.h>
#include <hardware/hardware.h>
#include <cutils/log.h>
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <math.h>
static const char THE_DEVICE[] = "/sys/class/timed_output/vibrator/enable";
static int vibra_exists() {
int fd;
return 1;
}
static int sendit(unsigned int timeout_ms)
{
return ret;
}
static int vibra_on(vibrator_device_t* vibradev __unused, unsigned int timeout_ms)
{
/* constant on, up to maximum allowed time */
return sendit(timeout_ms);
}
static int vibra_off(vibrator_device_t* vibradev __unused)
{
return sendit(0);
}
static int vibra_close(hw_device_t *device)
{
free(device);
return 0;
}
static int vibra_open(const hw_module_t* module, const char* id __unused,
hw_device_t** device __unused) {
if (!vibra_exists()) {
ALOGE("Vibrator device does not exist. Cannot start vibrator");
return -ENODEV;
}
vibrator_device_t *vibradev = calloc(1, sizeof(vibrator_device_t));
if (!vibradev) {
ALOGE("Can not allocate memory for the vibrator device");
return -ENOMEM;
}
vibradev->common.tag = HARDWARE_DEVICE_TAG;
vibradev->common.module = (hw_module_t *) module;// hw_module_t 就这点用?
vibradev->common.version = HARDWARE_DEVICE_API_VERSION(1,0);
vibradev->common.close = vibra_close;
vibradev->vibrator_on = vibra_on;
vibradev->vibrator_off = vibra_off;
*device = (hw_device_t *) vibradev;
return 0;
}
/*===========================================================================*/
/* Default vibrator HW module interface definition */
/*===========================================================================*/
static struct hw_module_methods_t vibrator_module_methods = {
.open = vibra_open,
};
struct hw_module_t HAL_MODULE_INFO_SYM = {
.id = VIBRATOR_HARDWARE_MODULE_ID,
.name = "Default vibrator HAL", //hw_get_module("led",...)
.methods = &vibrator_module_methods,
};
----------------------------------------------------------------------------------------------------------------------
LED 实例:
#ifndef _HARDWARE_LED_HAL_H
#define _HARDWARE_LED_HAL_H
#include <hardware/hardware.h>
__BEGIN_DECLS
struct led_device;
typedef struct led_device {
struct hw_device_t common;
int (*led_open)(struct led_device* leddev);
void (*led_close)(struct led_device* leddev);
int (*led_ctrl)(struct led_device* leddev, int which, int status);
} led_device_t;
__END_DECLS
#endif // _HARDWARE_VIBRATOR_H
JNI:
#define LOG_TAG "LedService"
#include "jni.h"
#include "JNIHelp.h"
#include "android_runtime/AndroidRuntime.h"
#include <utils/misc.h>
#include <utils/Log.h>
#include <stdio.h>
#include <hardware/led_hal.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <android/log.h> /* liblog */
static jint fd;
static hw_module_t* module;
static led_device_t* leddev;
static led_device_t* get_device(hw_module_t* module, char const* name)
{
int err;
hw_device_t* device;
err = module->methods->open(module, name, &device);
if (err == 0) {
return (led_device_t*)device;
} else {
return NULL;
}
}
jint ledOpen(JNIEnv *env, jobject cls)
{
ALOGI("native ledOpen");
if (hw_get_module("led", (hw_module_t const**)&module) == 0)
{
leddev = get_device(module, NULL);
}
leddev->led_open(leddev);
return fd;
}
void ledClose(JNIEnv *env, jobject cls, jint a, jint b)
{
leddev->led_close(leddev);
ALOGI("native ledClose");
}
jint ledCtrl(JNIEnv *env, jobject cls, jint which, jint status)
{
leddev->led_ctrl(leddev, status, which);
ALOGI("native ledCtrl which %d static %d", which, status);
return 0;
}
namespace android
{
static JNINativeMethod method_table[] = {
{ "native_Open", "()I", (void*)ledOpen },
{ "native_Close", "()V", (void*)ledClose },
{ "native_Ctrl", "(II)I", (void*)ledCtrl }
};
int register_android_server_LedService(JNIEnv *env)
{
return jniRegisterNativeMethods(env, "com/android/server/LedService",
method_table, NELEM(method_table));
}
};
#include <hardware/hardware.h>
#include <hardware/led_hal.h>
#include <cutils/log.h>
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <math.h>
#include <utils/Log.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/ioctl.h>
#include <android/log.h> /* liblog */
static int fd;
static int led_open(struct led_device* leddev)
{
ALOGI("led_open");
fd = open("/dev/leds", O_RDWR);
if (fd < 0)
{
ALOGI("led_open error");
}
return fd;
}
static int led_close(struct hw_device_t* device)
{
ALOGI("led_close");
close(fd);
return 0;
}
static int led_ctrl(struct led_device* leddev, int status, int which)
{
int ret = ioctl(fd, status, which);
ALOGI("led_ctrl which %d static %d", which, status);
return ret;
}
static int led_device_open(const hw_module_t* module, const char* id __unused,
hw_device_t** device __unused) {
led_device_t *leddev = calloc(1, sizeof(led_device_t));
if (!leddev) {
ALOGE("Can not allocate memory for the led device");
return -ENOMEM;
}
leddev->common.close = led_close;
leddev->led_open = led_open;
leddev->led_ctrl = led_ctrl;
*device = (hw_device_t *) leddev;
return 0;
}
static struct hw_module_methods_t led_module_methods = {
.open = led_device_open,
};
struct hw_module_t HAL_MODULE_INFO_SYM = {
.name = "led",
.methods = &led_module_methods,
};