硬件访问服务4之Android硬件访问服务框架及系统函数全详细实现


前面中,Android应用程序通过LoadLibary加载C库来访问硬件驱动程序,最终点亮或者熄灭LED
在C Library里面实现有一个JNI_Onload函数,一旦C Library被加载,JNI_Oniload就会被调用
在这个函数里,进行注册一些本地方法(jniRegisterNativeMethods),使得c语言里面实现的函数可以被java语言调用
在这些函数里面,Android应用程序ledOpen、ledClose、调用标准的c接口,open、ioctl、close等等来访问硬件驱动程序
上面这种方法是只有我们自己写的应用程序来访问它,可以使用这种方法来实现Android应用程序来实现对底层硬件的访问
称为通过JNI直接访问

问:如果有多个Android APP 来操作这个硬件呢?
答:那么我们只用一个应用程序来操作硬件-SystemServer
由SystemServer来统一操作硬件,其他应用程序把你对硬件的操作请求发给SystemServer

这就是硬件访问服务!
①:通过loadLibray:加载c库
②:在C库的JNI_Onload:注册本地方法
问:每个硬件的本地操作都不一样怎么办?
答:在(JNI_Onload的作用)JNI_Onload 里面分别调用各个硬件的函数来注册本地方法:LED\振动器\串口
③:SystemServer:对每个硬件构造Service,然后添加到系统:addService
④:APP怎么使用
首先获得服务:getService
然后使用服务:执行service方法

Server:服务器提供服务
Service:Server提供Service

框架图如下所示:
onload.cpp会调用很多硬件提供的函数,那些函数会注册不同的本地方法;

com_android_server_SerialService.cpp…可以称这些com.cpp为JNI文件!
HAL理解
如果硬件操作简单,可以在JNI文件里面调用OPEN、READ操作
但是如果操作繁杂,可以写在HAL文件里面,HAL即是硬件操作的意思
这样做的好处:
①:容易修改!
如果我修改了硬件操作,我只需要修改HAL文件,然后把对应的so文件放入系统里面就可以了。
如果你在JNI文件里面修改硬件操作的话,就需要编改整个系统,烧写整个系统,相当复杂
②:出于保密!
很多公司并不提供硬件操作程序,只提供.so文件,让你的JNI来加载so文件就可以使用了

继续分析流程!

以Vibrator进行例子分析:
在SystemServer.java函数里面有:

   Slog.i(TAG, "Vibrator Service");
   vibrator = new VibratorService(context);  //Garmen:实例化一个VibratorService
   ServiceManager.addService("vibrator", vibrator);//Garmen:addService:告诉系统

问:上面的告诉系统指的是什么?
答:其实系统就是上面流程框图中的service_manager.c
service_manager.c管理着系统里面所有的服务,比如SerilSerice、VibratorService、LedService
你要想这些这些Service被应用程序所使用,需要注册进service_manager里面
最后我们的应用程序可以向Service_manager查询获得某些Service

有一个差别:我们addService时候是LedService.java,但是getService时候是ILedService.java
后者I表明获得的是一个接口,应用程序APP通过这个接口,把对硬件的操作请求发送给该Service(例如LedService.java)
通过它来实现对硬件的操作

总共涉及三个进程:
**进程一:**SystemServer.java向service_manager.c进行addService注册添加各种Service
进程二:应用程序(实际上是一个客户端),Android app向service_manager.c查询获得服务
进程三:将服务请求发送给SystemServer.java

具体怎么实现硬件访问服务!:
①:写一个JNI和HAL程序
例如:
com_android_sever_LedService.cpp : 注册JNI本地函数
hal_led.c:open,read,write
com_android_sever_LedService.cpp会调用hal_led.c文件
②:修改onload.cpp : 调用com_android_sever_LedService.cpp这个文件实现的函数
③:修改SystemServer.java:
new LedService
addService
④:LedService.java:调用本地方法,实现硬件操作
⑤:ILedService.java:给App使用

硬件访问服务编写整套系统代码:

由简单到复杂来编写:

第一步:先编写上面的 ⑤ILedService.java:给App使用(实现一个接口文件,给应用程序使用)

我们只需要实现aidl文件,即Android接口定义语言
我们参考IVibatorServic.aidl来编写我们需要的文件

IVibatorServic.aidl:(非常简单)

package android.os;

/** {@hide} */
interface IVibratorService
{
    boolean hasVibrator();
    void vibrate(int uid, String opPkg, long milliseconds, int usageHint, IBinder token);
    void vibratePattern(int uid, String opPkg, in long[] pattern, int repeat, int usageHint, IBinder token);
    void cancelVibrate(IBinder token);
}

然后我们修改为自己所需要的:ILedService .aidl (只实现哪个灯,是什么状态)

package android.os;

/** {@hide} */
interface ILedService
{
    int ledCtrl1(int which, int status);

}

模仿Vibrate所在目录:frameworks\base\core\java\android\os
于是我们将我们所写的Led也放入该目录

然后进入系统,修改frameworks\base下的Android.mk
发现果然有很多


于是我们添加我们所写的LED:
core/java/android/os/ILedService.aidl

然后执行mmm命令,它会帮我们生成LedServer文件
编译结果放置在 out/文件里面!

我们将该ILedService.java提取可得到代码如下:

这个是自动生成的,不要进行修改: * This file is auto-generated. DO NOT MODIFY.

/* * 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;
}

第二步:我们要编写④中的LedService.java

继续参考振动器Vibrator得到我们自己修改的LedService.java
在LedService.java调用我们的native本地函数,并且记得在方法末尾进行本地native函数声明

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 int native_ledCtrl(int which, int status);
    public static int native_ledOpen();
    public static int native_ledClose();
}

第三步:修改SystemServer.java:

仿照Vibrator,进行修改:

            Slog.i(TAG, "Led Service");
            ServiceManager.addService("led", new LedService());

第四步:写出com_android_server_LedService.cpp: 注册本地方法,供LedService.java使用

#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>



namespace android {


static jint fd;

jint ledOpen(JNIEnv *env, jobject cls)
{
    fd = open("/dev/leds", O_RDWR);
    ALOGI("LEDDemo", "native ledOpen : %d", fd);
    if (fd >= 0)
        return 0;
    else
        return -1;
}

void ledClose(JNIEnv *env, jobject cls)
{
    ALOGI( "LEDDemo", "native ledClose ...");
    close(fd);
}


jint ledCtrl(JNIEnv *env, jobject cls, jint which, jint status)
{
    int ret = ioctl(fd, status, which);
    ALOGI("LEDDemo", "native ledCtrl : %d, %d, %d", which, status, ret);
    return ret;
}


static const JNINativeMethod methods[] = {
    {"native_ledOpen", "()I", (void *)ledOpen},
    {"native_ledClose", "()V", (void *)ledClose},
    {"native_ledCtrl", "(II)I", (void *)ledCtrl},
};



int register_android_server_LedService(JNIEnv *env)
{
    return jniRegisterNativeMethods(env, "com/android/server/LedService",
            methods, NELEM(methods));
}

};



第五步:修改onload.cpp

加上下面两项:

int register_android_server_LedService(JNIEnv *env);

extern "C" jint JNI_OnLoad(JavaVM* vm, void* reserved)
{
         register_android_server_LedService(env);

第六步:写Android应用程序

3 编写APP代码
a. 包含什么
出现问题1:出现有些我们写的JNI方法AS显示红色,即是我们并不能得到该方法,应该进行怎么包含???
解决方法:1
out/target/common/obj/JAVA_LIBRARIES/framework_intermediates/classes.jar
将classes.jar从已经进行编译过的服务器中拉出来,然后再在Android studio里面点击File->Project Structure->左上角添加Import JAR/.AAR Package
将classes.jar添加进去就完成了
然后再在Project Structure->左边栏目app的右上角+添加Module dependency->点击class

其实问题就是怎么搭建Android里面的隐藏类,具体解决方法见下面网页
How do I build the Android SDK with hidden and internal APIs available?
http://stackoverflow.com/questions/7888191/how-do-i-build-the-android-sdk-with-hidden-and-internal-apis-available

出现错误2:java.lang.OutOfMenoryError: GC overhead limit exceeded
解决方法2
在项目里面的app->bulid.gradle添加下列代码,然后同步sync now

dexOptions{
     javaMaxHeapSize "4g"
}

编译错误2. java.lang.OutOfMemoryError: GC overhead limit exceeded
解决方法参考的网站:
标题:Android Studio Google jar causing GC overhead limit exceeded error
网站:http://stackoverflow.com/questions/25013638/android-studio-google-jar-causing-gc-overhead-limit-exceeded-error

然后出现另外的错误3:Too many method references
解决方法3:
在app->bulid.gradle->修改defaultConfig->在这个方法里面添加下面代码

//Enabling multidex support 
multiDexEnabled true

再在app->bulid.gradle->修改dependencies->在这个方法里面添加下面代码

compile "com.android.support:multidex:1.0.0"

然后再app->src->main->AndroidMainifest.xma->application添加下面代码:

android:name="android.support.multidex.MultiDexApplication"

然后点击sync now
编译错误3. Too many field references
问题:Building Apps with Over 65K Methods
参考的网站:https://developer.android.com/tools/building/multidex.html

Android app代码如下:

以上方法是没有HAL代码的,直接用JNI操作硬件的

以下方法是写有HAL代码的,用JNI操作HAL函数然后进行硬件操作

第七步写HAL函数:

JNI:向上提供本地函数,向下加载HAL文件并调用HAL的函数
HAL:负责访问驱动程序执行硬件操作
JNI和HAL都是用c写的,所以JNI怎么来调用HAL,其实就是怎么使用dlopen函数来进行调用
现在是使用hw_get_module来替代dlopen

JNI 怎么使用 HAL
a. JNI调用hw_get_module 获得一个hw_module_t结构体
b. 调用 module->methods->open(module, device_name, &device)
hw_get_module已经传入一个模块(一个模块有多个设备)的ID,再从已知道ID里面通过device_name设备名字获得设备
获得一个hw_device_t结构体
并且把hw_device_t结构体转换为设备自定义的结构体

HAL 怎么写
a. 实现一个名为HMI的hw_module_t结构体
b. 实现一个open函数, 它会根据name返回一个设备自定义的结构体
这个设备自定义的结构体的第1个成员是 hw_device_t结构体,所以可以进行结构体类型转换
还可以定义设备相关的成员

分析hw_get_module函数流程:
external\chromium_org\third_party\hwcplus\src\hardware.c
hw_get_module(“led”)
1. 如何将模块名==>文件名

        hw_get_module_by_class("led", NULL)
            name = "led"
                property_get     xxx是某个属性
                hw_module_exists //判断是否存在led.xxx.so
1、函数hw_module_exists:
hw_module_exists(char *path, size_t path_len, const char *name,
                            const char *subname)
2、property_get : 属性系统
属性<键,值> <name, value>

上面的函数用来判断”name”.”subname”.so文件是否存在
查找的目录:
a. HAL_LIBRARY_PATH 环境变量
b. /vendor/lib/hw
c. /system/lib/hw

在上面三个目录查找有无下面的属性值
led.tiny4412.so
led.exynos4.so
led.default.so

一旦查找到有so文件,就用dlopen把文件加载进来

2. 如何加载
load
dlopen(filename)
dlsym(“HMI”) 从SO文件中获得名为HMI的hw_module_t结构体
strcmp(id, hmi->id) 判断名字是否一致(hmi->id, “led”)

Led_hal.c所写代码如下所示:

#define LOG_TAG "LedHal"


/* 1. 实现一个名为HMI的hw_module_t结构体 */

/* 2. 实现一个open函数, 它返回led_device_t结构体 */

/* 3. 实现led_device_t结构体 */

/* 参考 hardware\libhardware\modules\vibrator\vibrator.c */

#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 <hardware/led_hal.h>

#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <utils/Log.h>


static int fd;


/** Close this device */
static int led_close(struct hw_device_t* device)
{
    close(fd);
    return 0;
}

static int led_open(struct led_device_t* dev)
{
    fd = open("/dev/leds", O_RDWR);
    ALOGI("led_open : %d", fd);
    if (fd >= 0)
        return 0;
    else
        return -1;
}

static int led_ctrl(struct led_device_t* dev, int which, int status)
{
    int ret = ioctl(fd, status, which);
    ALOGI("led_ctrl : %d, %d, %d", which, status, ret);
    return ret;
}




static struct led_device_t led_dev = {
    .common = {
        .tag = HARDWARE_DEVICE_TAG,
        .close = led_close,
    },
    .led_open  = led_open,
    .led_ctrl  = led_ctrl,
};

static int led_device_open(const struct hw_module_t* module, const char* id,
        struct hw_device_t** device)
{
    *device = &led_dev;
    return 0;
}


static struct hw_module_methods_t led_module_methods = {
    .open = led_device_open,
};

struct hw_module_t HAL_MODULE_INFO_SYM = {
     .tag = HARDWARE_MODULE_TAG,
    .id = "led",
    .methods = &led_module_methods,
};


Led_hal.h所写代码如下所示:

#ifndef ANDROID_LED_INTERFACE_H
#define ANDROID_LED_INTERFACE_H

#include <stdint.h>
#include <sys/cdefs.h>
#include <sys/types.h>

#include <hardware/hardware.h>

__BEGIN_DECLS

struct led_device_t {
    struct hw_device_t common;

    int (*led_open)(struct led_device_t* dev);
    int (*led_ctrl)(struct led_device_t* dev, int which, int status);
};


__END_DECLS

#endif // ANDROID_LED_INTERFACE_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 <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <hardware/led_hal.h>


namespace android {

static led_device_t* led_device;

jint ledOpen(JNIEnv *env, jobject cls)
{
    jint err;
    hw_module_t* module;
    hw_device_t* device;

    ALOGI("native ledOpen ...");

    /* 1. hw_get_module */
    err = hw_get_module("led", (hw_module_t const**)&module);
    if (err == 0) {
        /* 2. get device : module->methods->open */
        err = module->methods->open(module, NULL, &device);
        if (err == 0) {
            /* 3. call led_open */
            led_device = (led_device_t *)device;
            return led_device->led_open(led_device);
        } else {
            return -1;
        }
    }

    return -1;    
}

void ledClose(JNIEnv *env, jobject cls)
{
    //ALOGI("native ledClose ...");
    //close(fd);
}


jint ledCtrl(JNIEnv *env, jobject cls, jint which, jint status)
{
    ALOGI("native ledCtrl %d, %d", which, status);
    return led_device->led_ctrl(led_device, which, status);
}


static const JNINativeMethod methods[] = {
    {"native_ledOpen", "()I", (void *)ledOpen},
    {"native_ledClose", "()V", (void *)ledClose},
    {"native_ledCtrl", "(II)I", (void *)ledCtrl},
};


int register_android_server_LedService(JNIEnv *env)
{
    return jniRegisterNativeMethods(env, "com/android/server/LedService",
            methods, NELEM(methods));
}

}

ledOpen流程:
首先获得module: err = hw_get_module(“led”, (hw_module_t const**)&module);
然后获得device: err = module->methods->open(module, NULL, &device);
得到我们想要的device:led_device = (led_device_t *)device;
调用device的open函数

到此为止,算是写完!
然后上传文件进行编译,过程如下!

(3) JNI: 重新上传
frameworks/base/services/core/jni/com_android_server_LedService.cpp

(4) HAL: led_hal.h、led_hal.c
把新文件上传到服务器, 所在目录:
hardware/libhardware/include/hardware/led_hal.h
hardware/libhardware/modules/led/led_hal.c
hardware/libhardware/modules/led/Android.mk

Android.mk内容如下:

# Copyright (C) 2012 The Android Open Source Project
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

LOCAL_PATH := $(call my-dir)

include $(CLEAR_VARS)

LOCAL_MODULE := led.default

# HAL module implementation stored in
# hw/<VIBRATOR_HARDWARE_MODULE_ID>.default.so
LOCAL_MODULE_RELATIVE_PATH := hw
LOCAL_C_INCLUDES := hardware/libhardware
LOCAL_SRC_FILES := led_hal.c
LOCAL_SHARED_LIBRARIES := liblog
LOCAL_MODULE_TAGS := eng

include $(BUILD_SHARED_LIBRARY)

编译:

$ mmm frameworks/base/services
$ mmm hardware/libhardware/modules/led
$ make snod
$ ./gen-img.sh

总结一下:
①:ILedService.aidl编译出了ILedService.java
但是App如何使用???
ILedService iLedService;
iLedService = ILedService.Stub.asInterface( ServiceManager.getService(“led”) )
以后就可以直接使用:iLedService.ledCtrl( 0 , 1);
但是这个iLedService方法并不会调用到我们写的c函数,它做的事情就是将服务请求发给LedService.java
所以说我们要写出LedService.java,在里面让它去调用led的硬件操作!!!

②:LedService.java
修改SystemServer.java
.addService(“led, new LedService()”);

③:com_android_Server_LedService.cpp
注册本地方法,供LedService.java使用

在服务器上进行编译的笔记:
(1) AIDL
1. 把 ILedService.aidl 放入 frameworks/base/core/java/android/os
2. 修改 frameworks/base/Android.mk 添加一行

         core/java/android/os/IVibratorService.aidl \
+        core/java/android/os/ILedService.aidl \
  1. mmm frameworks/base

  2. 它会生成: ./out/target/common/obj/JAVA_LIBRARIES/framework_intermediates/src/core/java/android/os/ILedService.java

(2) Server : LedService.java
SystemServer.java

把新文件上传到服务器, 所在目录:

frameworks/base/services/java/com/android/server/SystemServer.java
frameworks/base/services/core/java/com/android/server/LedService.java

不需要修改 frameworks/base/services/core/Android.mk
它的内容里已经把该目录下所有JAVA文件自动包含进去了:

LOCAL_SRC_FILES += \
    $(call all-java-files-under,java)

(3) JNI : com_android_server_LedService.cpp
onload.cpp

把新文件上传到服务器, 所在frameworks/base/services/core/jni/onload.cpp
frameworks/base/services/core/jni/com_android_server_LedService.cppframeworks/base/services/core/jni/Android.mk :

  $(LOCAL_REL_DIR)/com_android_server_VibratorService.cpp \
+ $(LOCAL_REL_DIR)/com_android_server_LedService.cpp \

编译:

$ mmm frameworks/base/services
$ make snod
$ ./gen-img.sh

你可能感兴趣的:(android)