您的位置:首页 > 移动开发 > Android开发

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

2017-05-28 14:37 555 查看


前面中,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
12585
()
{
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 \


mmm frameworks/base

它会生成:
./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
相关文章推荐