怎麼實現硬件訪問服務
1、JNI和HAL
com_andorid_server_ledService.cpp (JNI文件註冊JNI本地方法:供app應用程序調用)
hal_led.c (C庫:具體操作硬件接口函數實現)
2、修改onload.cpp 調用
com_andorid_server_ledService.cpp 中實現的函數register_andorid_server_ledService
3、修改systemServer.java
LedService ledservice = new ledService();
ServiceManager.addService( "led",ledservice );
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能夠調用的接口函數ledCtrl,這個接口函數需要在 LedService 類中實現
/*
* This file is auto-generated. DO NOT MODIFY.
* Original file: frameworks/base/./core/java/android/os/ILedService.aidl
*/
package android.os;
/** {@hide} */
public interface ILedService extends android.os.IInterface
{
/** Local-side IPC implementation stub class. */
public static abstract class Stub extends android.os.Binder implements android.os.ILedService
{
private static final java.lang.String DESCRIPTOR = "android.os.ILedService";
/** Construct the stub at attach it to the interface. */
public Stub()
{
this.attachInterface(this, DESCRIPTOR);
}
/**
* Cast an IBinder object into an android.os.ILedService interface,
* generating a proxy if needed.
*/
public static android.os.ILedService asInterface(android.os.IBinder obj)
{
if ((obj==null)) {
return null;
}
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if (((iin!=null)&&(iin instanceof android.os.ILedService))) {
return ((android.os.ILedService)iin);
}
return new android.os.ILedService.Stub.Proxy(obj);
}
@Override public android.os.IBinder asBinder()
{
return this;
}
@Override public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException
{
switch (code)
{
case INTERFACE_TRANSACTION:
{
reply.writeString(DESCRIPTOR);
return true;
}
case TRANSACTION_ledCtrl:
{
data.enforceInterface(DESCRIPTOR);
int _arg0;
_arg0 = data.readInt();
int _arg1;
_arg1 = data.readInt();
int _result = this.ledCtrl(_arg0, _arg1);
reply.writeNoException();
reply.writeInt(_result);
return true;
}
}
return super.onTransact(code, data, reply, flags);
}
private static class Proxy implements android.os.ILedService
{
private android.os.IBinder mRemote;
Proxy(android.os.IBinder remote)
{
mRemote = remote;
}
@Override public android.os.IBinder asBinder()
{
return mRemote;
}
public java.lang.String getInterfaceDescriptor()
{
return DESCRIPTOR;
}
@Override public int ledCtrl(int which, int status) throws android.os.RemoteException
{
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
int _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
_data.writeInt(which);
_data.writeInt(status);
mRemote.transact(Stub.TRANSACTION_ledCtrl, _data, _reply, 0);
_reply.readException();
_result = _reply.readInt();
}
finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
}
static final int TRANSACTION_ledCtrl = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
}
public int ledCtrl(int which, int status) throws android.os.RemoteException;
}
aidl文件編碼
添加ILedService.aidl文件
mmm編譯生成ILedService.java文件
APP如何使用ILedService提供的本地方法
ILedService iLedService = null; // 定義ILedService 類
// 獲取服務
iLedService = ILedService.Stub.asInterface(ServiceManager.getService("led"));
iLedService.ledCtrl( 0 , 1 ); // 調用接口
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 to access hardware */
public int ledCtrl(int which, int status) throws android.os.RemoteException
{
return native_ledCtrl(which, status);
}
public LedService() {
native_ledOpen();
}
public static native int native_ledOpen();
public static native void native_ledClose();
public static native int native_ledCtrl(int which, int status);
}
將 LedService.java 拷貝到 frameworks\base\services\core\java\com\android\server
無需 .mk 文件,frameworks\base\services\core\Android.mk 默認包含了全部 java 文件
如何註冊 Service 到 ServerManger ?
參考:SystemServer.java (frameworks\base\services\java\com\android\server)
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,
};
參考鏈接:https://blog.csdn.net/lizuobin2/article/details/73009142