android hal层学习笔记

android hal层分析

  • hal层接口定义
  • JNI接口注册
  • aidl文件定义java访问硬件的接口
  • 硬件访问service的注册
  • android应用程序中访问service操作硬件
  • 相关原理

hal层接口定义(硬件抽象层)

硬件抽象层是google为了满足各个厂商不想公开源码而又不违背GPL规定的一种方式,我们在这里讲解如何添加hal层,当然我们这里为了简化没有实际的访问硬件,我在这里用一个变量(hello_number)当成是我们的硬件,首先hal层的定义有一些标准,这里我们先通过例子说明然后再将原理
首先杂货android源码目录hardware/libhardware/include/hardware下新建文件hello.h,内容如下

//目录:hardware/libhardware/include/hardware/hello.h

#ifndef ANDROID_HELLO_INTERFACE_H  
#define ANDROID_HELLO_INTERFACE_H  
#include   

__BEGIN_DECLS  

/*定义模块ID*/  
#define HELLO_HARDWARE_MODULE_ID "hello"  

/*硬件模块结构体*/  
struct hello_module_t {  
    struct hw_module_t common;  
};  

/*硬件接口结构体*/  
struct hello_device_t {  
    struct hw_device_t common;  
    int fd;  
    int (*set_value)(struct hello_device_t* dev, int val);  
    int (*get_value)(struct hello_device_t* dev, int* val);  
};  

__END_DECLS  

#endif  

接着在hardware/libhardware/modules目录下建立hello目录然后建立hello.c内容如下:

//目录:hardware/libhardware/modules/hello/hello.c
#define LOG_TAG "HelloStub"  

#include   
#include   
#include   
#include   
#include   
#include   

#define MODULE_NAME "Hello"  
#define MODULE_AUTHOR "jinpeng_he"
/*设备打开和关闭接口*/  
static int hello_number;
static int hello_device_open(const struct hw_module_t* module, const char* name, struct hw_device_t** device);  
static int hello_device_close(struct hw_device_t* device);  

/*设备访问接口*/  
static int hello_set_value(struct hello_device_t* dev, int val);  
static int hello_get_value(struct hello_device_t* dev, int* val);  

/*模块方法表*/  
static struct hw_module_methods_t hello_module_methods = {  
    open: hello_device_open  
};  

/*模块实例变量*/  
struct hello_module_t HAL_MODULE_INFO_SYM = {  
    common: {  
        tag: HARDWARE_MODULE_TAG,  
        version_major: 1,  
        version_minor: 0,  
        id: HELLO_HARDWARE_MODULE_ID,  
        name: MODULE_NAME,  
        author: MODULE_AUTHOR,  
        methods: &hello_module_methods,  
    }  
};  
static int hello_device_open(const struct hw_module_t* module, const char* name, struct hw_device_t** device) {  
    struct hello_device_t* dev;dev = (struct hello_device_t*)malloc(sizeof(struct hello_device_t));  

    if(!dev) {  
        LOGE("Hello Stub: failed to alloc space");  
        return -EFAULT;  
    }  

    memset(dev, 0, sizeof(struct hello_device_t));  
    dev->common.tag = HARDWARE_DEVICE_TAG;  
    dev->common.version = 0;  
    dev->common.module = (hw_module_t*)module;  
    dev->common.close = hello_device_close;  
    dev->set_value = hello_set_value;
    dev->get_value = hello_get_value;  


    *device = &(dev->common);  
    LOGI(" hello hal successfully.");  

    return 0;  
}  

static int hello_device_close(struct hw_device_t* device) {  
    struct hello_device_t* hello_device = (struct hello_device_t*)device;  

    if(hello_device) {  
          LOGI(" hello hal close.");
    }  
    return 0;  
} 
static int hello_set_value(struct hello_device_t* dev, int val) {  
    LOGI(" set value %d to device hello_number.", val);  
    hello_number=val;

    return 0;  
}  
static int hello_get_value(struct hello_device_t* dev, int* val) {  
    if(!val) {  
        LOGE("Hello : error val pointer");  
        return -EFAULT;  
    }  

    *val=hello_number; 

    LOGI(" get value %d from device", *val);  

    return 0;  
}  

在hello目录底下创建Android.mk内容如下:

//目录:hardware/libhardware/modules/hello/Android.mk
 LOCAL_PATH := $(call my-dir)
      include $(CLEAR_VARS)
      LOCAL_MODULE_TAGS := optional
      LOCAL_PRELINK_MODULE := false
      LOCAL_MODULE_PATH := $(TARGET_OUT_SHARED_LIBRARIES)/hw
      LOCAL_SHARED_LIBRARIES := liblog
      LOCAL_SRC_FILES := hello.c
      LOCAL_MODULE := hello.default
      include $(BUILD_SHARED_LIBRARY)

编译:在android源码目录底下执行:
mmm hardware/libhardware/modules/hello
编译成功之后,在out/target/product/generic/system/lib/hw目录下看到hello.default.so。注:有可能这里不是generic各个公司会有自己的目录,这个和makefile环境设置有关

这里主要生成一个动态库,注意hal层接口定义的要求,详细可以查看相关资料不是很复杂

JNI接口注册

前面我们已经生成了hal层动态库,这里我们在hal层调用这个动态库,将我们的方法注册进java虚拟机
进入到frameworks/base/services/jni目录,新建com_android_server_HelloService.cpp文件,内容如下:

#define LOG_TAG "HelloService"  
#include "jni.h"  
#include "JNIHelp.h"  
#include "android_runtime/AndroidRuntime.h"  
#include   
#include   
#include   
#include   
#include   

namespace android  
{  
    /*在硬件抽象层中定义的硬件访问结构体,参考*/  
        struct hello_device_t* hello_device = NULL;  
    /*通过硬件抽象层定义的硬件访问接口设置硬件寄存器val的值*/  
        static void hello_setVal(JNIEnv* env, jobject clazz, jint value) {  
        int val = value;  
        LOGI("Hello JNI: set value %d to device.", val);  
        if(!hello_device) {  
            LOGI("Hello JNI: device is not open.");  
            return;  
        }  

        hello_device->set_value(hello_device, val);  
    }  
        /*通过硬件抽象层定义的硬件访问接口读取硬件寄存器val的值*/  
    static jint hello_getVal(JNIEnv* env, jobject clazz) {  
        int val = 0;  
        if(!hello_device) {  
            LOGI("Hello JNI: device is not open.");  
            return val;  
        }  
        hello_device->get_value(hello_device, &val);  

        LOGI("Hello JNI: get value %d from device.", val);  

        return val;  
    }  
        /*通过硬件抽象层定义的硬件模块打开接口打开硬件设备*/  
    static inline int hello_device_open(const hw_module_t* module, struct hello_device_t** device) {  
        return module->methods->open(module, HELLO_HARDWARE_MODULE_ID, (struct hw_device_t**)device);  
    }  
        /*通过硬件模块ID来加载指定的硬件抽象层模块并打开硬件*/  
    static jboolean hello_init(JNIEnv* env, jclass clazz) {  
        hello_module_t* module;  

        LOGI("Hello JNI: initializing......");  
        if(hw_get_module(HELLO_HARDWARE_MODULE_ID, (const struct hw_module_t**)&module) == 0) {  
            LOGI("Hello JNI: hello Stub found.");  
            if(hello_device_open(&(module->common), &hello_device) == 0) {  
                LOGI("Hello JNI: hello device is open.");  
                return 0;  
            }  
            LOGE("Hello JNI: failed to open hello device.");  
            return -1;  
        }  
        LOGE("Hello JNI: failed to get hello module.");  
        return -1;        
    }  
        /*JNI方法表*/  
    static const JNINativeMethod method_table[] = {  
        {"init_native", "()Z", (void*)hello_init},  
        {"setVal_native", "(I)V", (void*)hello_setVal},  
        {"getVal_native", "()I", (void*)hello_getVal},  
    };  
        /*注册JNI方法*/  
    int register_android_server_HelloService(JNIEnv *env) {  
            return jniRegisterNativeMethods(env, "com/android/server/HelloService", method_table, NELEM(method_table));  
    }  
};  

在hello_init函数中,通过Android硬件抽象层提供的hw_get_module方法来加载模块ID为HELLO_HARDWARE_MODULE_ID的硬件抽象层模块,其中,HELLO_HARDWARE_MODULE_ID是在

namespace android {
    。。。。。

      int register_android_server_HelloService(JNIEnv *env);
};
 extern "C" jint JNI_onLoad(JavaVM* vm, void* reserved)
 {
       。。。。。
       register_android_server_HelloService(env);
 }

修改frameworks/base/services/jni/Android.mk,添加如下内容
com_android_server_HelloService.cpp

LOCAL_SRC_FILES:= \
      com_android_server_AlarmManagerService.cpp \
      com_android_server_BatteryService.cpp \
      com_android_server_InputManager.cpp \
      。。。。。。
      com_android_server_HelloService.cpp /
      onload.cpp

编译mmm frameworks/base/services/jni
重新打包system.img:make snod
通过上面这几步我们已经将JNI层的接口定义好,烧写系统之后,我们的JNI层接口就会注册进虚拟机(因为我们在onload.cpp里面注册了这个会在系统启动虚拟机的时候将这些JNI方法注册起来)。到这里我们只是将我们的接口注册好了,但是还没有调用,所以下面就是在systemservice这个进程里面添加我们调用JNI接口的service,并且启动service,之后,我们就可以通过应用程序访问我们的硬件(这里用hello_number模拟)

aidl文件定义java访问硬件的接口

什么是aidl文件呢?翻译过来是android 接口定义语言,通俗点讲,我的理解是这样的,因为我们aidl通常用于android binder进程间通信,binder进程间通信接口定义有一定的标准,所以既然是标准那么就相对比较固定,这样就可以自动化实现,所以就定义了这个aidl,我们只要在aidl里面定义好我们的接口,那么对应的接口,那么我们用于binder进程间通信的一些接口类就会自动生成这样减少了工程师的工作量。说白了就是代码自动生成功能
下面是我们定义的aidl文件
frameworks/base/core/java/android/os/IHelloService.aidl,内容如下:

package android.os;  

interface IHelloService {  
    void setValue(int val);  
    int getValue();  
} 

frameworks/base/Android.mk文件,修改LOCAL_SRC_FILES变量的增加我们这里定义的aidl文件

 LOCAL_SRC_FILES += /
    。。。。
   core/java/android/os/IHelloService.aidl /
   。。。。

编译:mmm frameworks/base
就会根据IHelloService.aidl生成相应的IHelloService.Stub接口

新增frameworks/base/services/java/com/android/server/HelloService.java内容如下:

package com.android.server;  
import android.content.Context;  
import android.os.IHelloService;  
import android.util.Slog;  
public class HelloService extends IHelloService.Stub {  
    private static final String TAG = "HelloService";  
    HelloService() {  
        init_native();  
    }  
    public void setVal(int val) {  
        setVal_native(val);  
    }     
    public int getVal() {  
        return getVal_native();  
    }  

    private static native boolean init_native();  
        private static native void setVal_native(int val);  
    private static native int getVal_native();  
};  

由于HelloService继承IHelloService.Stub所以他就天然的有了binder进程间通信的接口(aidl文件编译生成)
到这里我们访问硬件的service就算定义好了,我们只用将这个service在systemserver这个进程里面注册进servicemanager就可以了

@Override

 public void run() {
     。。。。。
     try {

          Slog.i(TAG, "Hello Service");

          ServiceManager.addService("hello", new HelloService());

            } catch (Throwable e) {

              Slog.e(TAG, "Failure starting Hello Service", e);
            }
 }

编译HelloService和重新打包system.img
mmm frameworks/base/services/java
make snod
然后烧录我们的HelloService就注册进servicemanager这个进程了,之后我们的应用程序只需要通过binder进程间通信就可以访问我们的HelloService,也就是访问我们的硬件了。

android应用程序中访问service操作硬件

package jinpeng.he.hello;  

import android.app.Activity;  
import android.os.ServiceManager;  
import android.os.Bundle;  
import android.os.IHelloService;  
import android.os.RemoteException;  
import android.util.Log;  
import android.view.View;  
import android.view.View.OnClickListener;  
import android.widget.Button;  
import android.widget.EditText;  

public class Hello extends Activity implements OnClickListener {  
    private final static String LOG_TAG = "jinpeng.he.Hello";  

    private IHelloService helloService = null;  

    private EditText valueText = null;  
    private Button readButton = null;  
    private Button writeButton = null;  
    private Button clearButton = null;  

    /** Called when the activity is first created. */  
    @Override  
    public void onCreate(Bundle savedInstanceState) {  
        super.onCreate(savedInstanceState);  
        setContentView(R.layout.main);  

    helloService = IHelloService.Stub.asInterface(  
        ServiceManager.getService("hello"));  

        valueText = (EditText)findViewById(R.id.edit_value);  
        readButton = (Button)findViewById(R.id.button_read);  
        writeButton = (Button)findViewById(R.id.button_write);  
        clearButton = (Button)findViewById(R.id.button_clear);  

    readButton.setOnClickListener(this);  
    writeButton.setOnClickListener(this);  
    clearButton.setOnClickListener(this);  

        Log.i(LOG_TAG, "Hello Activity Created");  
    }  

    @Override  
    public void onClick(View v) {  
        if(v.equals(readButton)) {  
        try {  
                int val = helloService.getVal();  
                String text = String.valueOf(val);  
                valueText.setText(text);  
        } catch (RemoteException e) {  
            Log.e(LOG_TAG, "Remote Exception while reading value from device.");  
        }         
        }  
        else if(v.equals(writeButton)) {  
        try {  
                String text = valueText.getText().toString();  
                int val = Integer.parseInt(text);  
            helloService.setVal(val);  
        } catch (RemoteException e) {  
            Log.e(LOG_TAG, "Remote Exception while writing value to device.");  
        }  
        }  
        else if(v.equals(clearButton)) {  
            String text = "";  
            valueText.setText(text);  
        }  
    }  
}  

将该工程文件放在packages/experimental/目录底下并修改该目录底下的Android.mk:

LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE_TAGS := optional
LOCAL_SRC_FILES := $(call all-subdir-java-files)
LOCAL_PACKAGE_NAME := Hello
include $(BUILD_PACKAGE)

编译:mmm packages/experimental/Hello
打包system.img:make snod
这样system.img里面就内置了Hello.apk
烧录启动系统之后就可以通过这个apk操作硬件(这里是hello_number变量)

相关原理

1,进程间通信,在我们这里一共牵扯到了zygote,systemserver,servicemanager,还有我们的应用程序这四个进程
2,我在这里模拟一下这几个进程的关系
首先系统启动,内核将各个driver注册起来之后启动第一个进程init进程,在init进程启动之后会启动zygote进程。
zygote是一个比较重要的进程他会注册我们的JNI接口,在这里就是JNI调用hal层操作硬件的接口,然后启动虚拟机,紧接着启动systemserver进程。(其实我们android所有应用程序都是zygote通过系统调用fork出来的)。
systemserver进程启动之后通过ServiceManager.addService(“hello”, new HelloService());将我们的service注册进servicemanager进程。
servicemanager进程是一个native进程用C实现进程,当然在java有binder通信接口。这个servicemanager进程就是后台不断的接收应用的添加service,请求服务等通信。就是一个死循环,不过如果没有请求就休眠。
我们的应用程序通过helloService = IHelloService.Stub.asInterface(
ServiceManager.getService(“hello”)); 实际就是通过binder进程间通信让servicemanager找到我们注册的helloservice,然后我们就可以通过helloService调用我们在service里面定义的接口了。当然也是代理接口

你可能感兴趣的:(Android)