- 硬件平台:tiny4412
- 系统:Android 5.0.2
- 编译器: arm-linux-gcc-4.5.1
上一节我们通过JNI实现了应用程序对硬件的操作,思路是:应用程序中我们申明了本地方法,并通过System.loadLibrary("hardcontrol"); 来加载C库,loadLibrary的同时会调用C库中JNI_OnLoad方法,来注册本地方法,这个时候应用程序就可以通过调用申明的方法来调用C库中对应的方法,从而实现对硬件的访问。然后Android源码中并没有采用这种方式,而是采用了硬件访问服务来操作硬件,具体什么是硬件访问服务呢,这一节我们来详细学习。
首先来看一下硬件访问服务的框图:
一、硬件访问服务实现流程分析:
1、整体思路
(1)硬件访问服务,通过SystemServer来访问硬件,SystemServer通过loadlibrary来加载C库。
(2)同时C库的JNI_OnLoad函数被调用,里面分别调用硬件的JNI函数注册本地方法(JNI调用HAL层)。
(3)SystemServer对每个硬件,构造service,如:vibrator = new VibratorService(context);
(4)然后向ServiceManager里addService。如:ServiceManager.addService("vibrator", vibrator);
(5)应用程序获得service,然后调用里面的方法。
2、具体分析
(1)系统上电后,会执行SystemServer
SystemServer.java有一个主函数,里面调用run方法。
public static void main(String[] args) {
new SystemServer().run();
}
主函数执行run。Run中loadLibrary加载c库:
// Initialize native services.
System.loadLibrary("android_servers");
nativeInit();
android_servers对应的是onload.cpp,里面有JNI_OnLoad函数。loadlibrary的同时JNI_OnLoad被调用
#include "JNIHelp.h"
#include "jni.h"
#include "utils/Log.h"
#include "utils/misc.h"
namespace android {
int register_android_server_AlarmManagerService(JNIEnv* env);
int register_android_server_AssetAtlasService(JNIEnv* env);
int register_android_server_BatteryStatsService(JNIEnv* env);
int register_android_server_ConsumerIrService(JNIEnv *env);
int register_android_server_InputApplicationHandle(JNIEnv* env);
int register_android_server_InputWindowHandle(JNIEnv* env);
int register_android_server_InputManager(JNIEnv* env);
int register_android_server_LightsService(JNIEnv* env);
int register_android_server_PowerManagerService(JNIEnv* env);
int register_android_server_SerialService(JNIEnv* env);
int register_android_server_SystemServer(JNIEnv* env);
int register_android_server_UsbDeviceManager(JNIEnv* env);
int register_android_server_UsbHostManager(JNIEnv* env);
int register_android_server_VibratorService(JNIEnv* env);
int register_android_server_location_GpsLocationProvider(JNIEnv* env);
int register_android_server_location_FlpHardwareProvider(JNIEnv* env);
int register_android_server_connectivity_Vpn(JNIEnv* env);
int register_android_server_hdmi_HdmiCecController(JNIEnv* env);
int register_android_server_tv_TvInputHal(JNIEnv* env);
int register_android_server_PersistentDataBlockService(JNIEnv* env);
int register_android_server_fingerprint_FingerprintService(JNIEnv* env);
int register_android_server_Watchdog(JNIEnv* env);
};
using namespace android;
extern "C" jint JNI_OnLoad(JavaVM* vm, void* reserved)
{
JNIEnv* env = NULL;
jint result = -1;
if (vm->GetEnv((void**) &env, JNI_VERSION_1_4) != JNI_OK) {
ALOGE("GetEnv failed!");
return result;
}
ALOG_ASSERT(env, "Could not retrieve the env!");
register_android_server_PowerManagerService(env);
register_android_server_SerialService(env);
register_android_server_InputApplicationHandle(env);
register_android_server_InputWindowHandle(env);
register_android_server_InputManager(env);
register_android_server_LightsService(env);
register_android_server_AlarmManagerService(env);
register_android_server_UsbDeviceManager(env);
register_android_server_UsbHostManager(env);
register_android_server_VibratorService(env);
register_android_server_SystemServer(env);
register_android_server_location_GpsLocationProvider(env);
register_android_server_location_FlpHardwareProvider(env);
register_android_server_connectivity_Vpn(env);
register_android_server_AssetAtlasService(env);
register_android_server_ConsumerIrService(env);
register_android_server_BatteryStatsService(env);
register_android_server_hdmi_HdmiCecController(env);
register_android_server_tv_TvInputHal(env);
register_android_server_PersistentDataBlockService(env);
register_android_server_fingerprint_FingerprintService(env);
register_android_server_Watchdog(env);
return JNI_VERSION_1_4;
}
JNI_OnLoad里面实现注册各个本地服务。
// Start services.
try {
startBootstrapServices();
startCoreServices();
startOtherServices();
} catch (Throwable ex) {
Slog.e("System", "******************************************");
Slog.e("System", "************ Failure starting system services", ex);
throw ex;
}
(3)startOtherServices会注册服务到ServiceManager里面
Slog.i(TAG, "Vibrator Service");
vibrator = new VibratorService(context);
ServiceManager.addService("vibrator", vibrator);
(4)VibratorService会调用本地方法。
二、怎么实现硬件访问服务
(1)需要写一个JNI和HAL。
JNI:com_android_server_VibratorService.cpp (frameworks\base\services\core\jni)里面加载HAL。
HAL:Vibrator.c (hardware\libhardware\modules\vibrator)里面实现对硬件的操作。
(2)修改onload.cpp,添加JNI函数调用。
(3)修改SystemServer.java,new服务,并add到ServiceManager中
(4)想实现上面的newLedService(),必须写出这个服务。实现LedService,里面调用本地方法。
(5)应用程序要获取ILedService接口,供应用程序使用。
三、代码实现
3.1编写aldl文件
实现aldl文件,最简单,给APP使用的接口。
通过.aldl自动生成接口.java文件,实现了进程间的通讯。
参考IVibratorservice.aldl文件编写ILedService.ALDL代码放到,
/android-5.0.2/frameworkds/base/core/java/android/os目录下
(1)ILedService.ALDl
实现与APP直接对接的操作硬件的接口,这个最简单。只需要实现AIDL文件(Android Interface Define Language),其目的是让安卓系统自动帮我们实现对应用JAVA接口文件。PS:可以搜索源码中的例子比如:IVibratorservice.aidl
package android.os;
/** {@hide} */
interface ILedService
{
int ledCtrl(int which, int status);
}
(2)上传AIDL文件
将AIDL文件上传编译之前,需要先编译整个安卓源码(目前编译4412开发板)。编译完后上传到目录中,But,哪个目录?模仿其它AIDL文件在哪个目录,然后上传到相应的目录。/android-5.0.2/frameworkds/base/core/java/android/os目录下
逐层查找android.mk
(3)修改/android-5.0.2/frameworkds/base/下的Android.mk文件:
增加一行:core/java/android/os/ILedService.aidl\(yy复制当前行 p粘贴)
(4)android-5.0.2/frameworkds/base/下输入mmm .回车编译当前目录/android-5.0.2/frameworkds/base
(5)编译的结果放在/android-5.0.2/out/下find -name "ILedService.java"。里面实现了
public interface ILedService extends android.os.IInterface
public static abstract class Stub extends android.os.Binder implements android.os.ILedService
public int ledCtrl(int which, int status) throws android.os.RemoteException;
如何使用参考:内核搜索IVibratorService.aidl
private final IVibratorService mService;
public SystemVibrator() {
mService = IVibratorService.Stub.asInterface(
ServiceManager.getService("vibrator"));
}。。。
mService.hasVibrator();
(6)ILedService.java这个文件接口如何使用。
先获取Service,然后将其转换为接口对象,然后调用这个接口对象的成员方法。
总结:
l 编写ILedService.aidl文件 ==>编译生成ILedService.java文件
l app使用:ILedService iLedService;
iLedService = ILedService.Stub.asInterface(ServiceManager.getService("Led"))
调用:iLedService.ledctrl(0,1);
所以用户APP层调用iLedService接口,但是这个接口并不直接操作硬件,iLedService会把服务请求发给LedService
操作硬件的是LedService.java,下面描述这个LedService.java程序怎么编写。注意,这个LedService.java的作用是调用本地Native方法来操作硬件。
3.2编写LedService.java文件
编写LedService.java 参考VibratorService.java
(frameworks/base/services/core/java/com/android/server/LedService.java)
ILedService.Stub类里面有ledCtrl方法,这里也实现。
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 native int native_ledOpen();
public static native void native_ledClose();
public static native int native_ledCtrl(int which, int status);
}
3.3修改SystemServer.java
Slog.i(TAG, "Vibrator Service");
vibrator = new VibratorService(context);
ServiceManager.addService("vibrator", vibrator);
根据vibrator = new VibratorService(context);
Slog.i(TAG, "led Service");
ServiceManager.addService("led", new LedService());
3.4写com_android_LedService.cpp 称为JNI文件
参考com_ android_VibratorService.cpp
#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>
namespace android
{
static jint fd;
jint ledOpen(JNIEnv *env, jobject cls)
{
fd = open("/dev/leds", O_RDWR);
ALOGI("native ledOpen : %d", fd);
if (fd >= 0)
return 0;
else
return -1;
}
void ledClose(JNIEnv *env, jobject cls)
{
ALOGI("native ledClose ...");
close(fd);
}
jint ledCtrl(JNIEnv *env, jobject cls, jint which, jint status)
{
int ret = ioctl(fd, status, which);
ALOGI("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)
register_android_server_VibratorService(env);
register_android_server_LedService(env);
$ mmm frameworks/base/services(包含了JNI的内容)
$ android-5.0.2/ 下make snod//生成映像文件system.image
$ ./gen-img.sh 同步system.image
3.5修改编写总结
(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 \
3. mmm frameworks/base
4. 它会生成:
./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.cpp
修改frameworks/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.sh3.6下载system.image测试
http://download.csdn.net/detail/fengyuwuzu0519/9756313