前面中,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_DECLSstruct 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.soLOCAL_MODULE_RELATIVE_PATH := hwLOCAL_C_INCLUDES := hardware/libhardwareLOCAL_SRC_FILES := led_hal.cLOCAL_SHARED_LIBRARIES := liblogLOCAL_MODULE_TAGS := enginclude $(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.javaframeworks/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

更多相关文章

  1. Android 4.0的图形硬件加速及绘制技巧
  2. [置顶] Android 从硬件到应用:一步一步向上爬 4 -- 使用 JNI 方法
  3. Android 文件操作心得体会
  4. 海康威视视频监控demo 源码+库文件
  5. [置顶] 开发Android硬件抽象层模块接口
  6. Android studio 添加assets文件夹
  7. android R 文件生成不了
  8. Android 实现文件上传功能(upload)
  9. Android清单文件详解(六) ---- 节点的属性

随机推荐

  1. android 获取手机位置信息
  2. Android 官方数据库Room --- 配置
  3. Android:根据上下文Context获取Activity
  4. 源码网站推荐
  5. 【Android】打开/读取文件的方法
  6. Android的设计模式
  7. Android中View(视图)绘制不同状态背景图
  8. 记住密码
  9. Android传感器API:近距离感应Proximity
  10. Android新增keycode