Android系統硬件訪問服務框架分析

 

怎麼實現硬件訪問服務
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

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章