前面中,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使用
由简单到复杂来编写:
我们只需要实现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;
}
继续参考振动器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();
}
仿照Vibrator,进行修改:
Slog.i(TAG, "Led Service");
ServiceManager.addService("led", new LedService());
#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));
}
};
加上下面两项:
int register_android_server_LedService(JNIEnv *env);
extern "C" jint JNI_OnLoad(JavaVM* vm, void* reserved)
{
register_android_server_LedService(env);
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函数然后进行硬件操作
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