文章导读:HAL硬件抽象层的实现及架构、Android API 与硬件平台的衔接、NDK的一些看法。
简介:Hardware Abstraction Layer 硬件抽象层是一个轻量级(lightweight)的的运行环境,提供了简单的设备驱动程序接口,应用程序使用设备驱动程序与底层硬件之间进行通信。HAL应用程序接口和ANSIC标准库结合在一起,这样用户可以使用C语言库函数来访问Android文件系统。下图是其直观的概念:
从图中,我们可以看到HAL是基于Linux Kernel与Libraries和Android Runtime之间。也就是说,HAL是底层硬件设备驱动程序暴露给Application Framework (也就是通常我们使用的Android API )的一个接口层。(可以浏览http://hi.baidu.com/aokikyon/blog/item/a66e0f87d8f55326c75cc32b.html HAL分析报告获得更详细的认识)
在网上也看到一些朋友写了重力感应器的api使用,那么以重力感应器Sensor为例子,看看重力感应器如何和Applications、Application Framework衔接。
1、下面Sensors.h的定义重力感应器对驱动程序部分的操作:
在源码./platform/hardware/Libardware /Include/Sensors.h目录下
#include <...>
__BEGIN_DECLS /** * The id of this module */ #define SENSORS_HARDWARE_MODULE_ID "sensors" /** * Name of the sensors device to open */ #define SENSORS_HARDWARE_CONTROL "control" #define SENSORS_HARDWARE_DATA "data" ..... /** convenience API for opening and closing a device */ static inline int sensors_control_open(const struct hw_module_t* module, struct sensors_control_device_t** device) { return module->methods->open(module, SENSORS_HARDWARE_CONTROL, (struct hw_device_t**)device); } static inline int sensors_control_close(struct sensors_control_device_t* device) { return device->common.close(&device->common); } static inline int sensors_data_open(const struct hw_module_t* module, struct sensors_data_device_t** device) { return module->methods->open(module, SENSORS_HARDWARE_DATA, (struct hw_device_t**)device); } static inline int sensors_data_close(struct sensors_data_device_t* device) { return device->common.close(&device->common); } __END_DECLS #endif // ANDROID_SENSORS_INTERFACE_H
2、JNI部分代码
加载该驱动的访问程序./framework/Jni/onLoad.cpp
#include "jni.h"
#include "utils/Log.h"
#include "utils/misc.h"
#include "JNIHelp.h"
namespace android {
...
int register_android_server_SensorService(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) {
LOGE("GetEnv failed!");
return result;
}
LOG_ASSERT(env, "Could not retrieve the env!");
//注册SensorService
register_android_server_SensorService(env);
return JNI_VERSION_1_4;
}
向Application Framework提供接口部分./framework/Jni/com_android_server_SensorService.cpp
#define LOG_TAG "SensorService"
#define LOG_NDEBUG 0
#include "utils/Log.h"
//加载sensor.h文件
#include <hardware/sensors.h>
#include "jni.h"
#include "JNIHelp.h"
namespace android {
static struct file_descriptor_offsets_t
{
jclass mClass;
jmethodID mConstructor;
jfieldID mDescriptor;
} gFileDescriptorOffsets;
static struct parcel_file_descriptor_offsets_t
{
jclass mClass;
jmethodID mConstructor;
} gParcelFileDescriptorOffsets;
static struct bundle_descriptor_offsets_t
{
jclass mClass;
jmethodID mConstructor;
jmethodID mPutIntArray;
jmethodID mPutParcelableArray;
} gBundleOffsets;
/*
* The method below are not thread-safe and not intended to be
*/
static sensors_control_device_t* sSensorDevice = 0;
static jint
android_init(JNIEnv *env, jclass clazz)
{
sensors_module_t* module;
if (hw_get_module(SENSORS_HARDWARE_MODULE_ID, (const hw_module_t**)&module) == 0) {
if (sensors_control_open(&module->common, &sSensorDevice) == 0) {
const struct sensor_t* list;
int count = module->get_sensors_list(module, &list);
return count;
}
}
return 0;
}
static jobject
android_open(JNIEnv *env, jclass clazz)
{
native_handle_t* handle = sSensorDevice->open_data_source(sSensorDevice);
if (!handle) {
return NULL;
}
// new Bundle()
jobject bundle = env->NewObject(
gBundleOffsets.mClass,
gBundleOffsets.mConstructor);
if (handle->numFds > 0) {
jobjectArray fdArray = env->NewObjectArray(handle->numFds,
gParcelFileDescriptorOffsets.mClass, NULL);
for (int i = 0; i < handle->numFds; i++) {
// new FileDescriptor()
jobject fd = env->NewObject(gFileDescriptorOffsets.mClass,
gFileDescriptorOffsets.mConstructor);
env->SetIntField(fd, gFileDescriptorOffsets.mDescriptor, handle->data[i]);
// new ParcelFileDescriptor()
jobject pfd = env->NewObject(gParcelFileDescriptorOffsets.mClass,
gParcelFileDescriptorOffsets.mConstructor, fd);
env->SetObjectArrayElement(fdArray, i, pfd);
}
// bundle.putParcelableArray("fds", fdArray);
env->CallVoidMethod(bundle, gBundleOffsets.mPutParcelableArray,
env->NewStringUTF("fds"), fdArray);
}
if (handle->numInts > 0) {
jintArray intArray = env->NewIntArray(handle->numInts);
env->SetIntArrayRegion(intArray, 0, handle->numInts, &handle->data[handle->numInts]);
// bundle.putIntArray("ints", intArray);
env->CallVoidMethod(bundle, gBundleOffsets.mPutIntArray,
env->NewStringUTF("ints"), intArray);
}
// delete the file handle, but don't close any file descriptors
native_handle_delete(handle);
return bundle;
}
static jint
android_close(JNIEnv *env, jclass clazz)
{
if (sSensorDevice->close_data_source)
return sSensorDevice->close_data_source(sSensorDevice);
else
return 0;
}
static jboolean
android_activate(JNIEnv *env, jclass clazz, jint sensor, jboolean activate)
{
int active = sSensorDevice->activate(sSensorDevice, sensor, activate);
return (active<0) ? false : true;
}
static jint
android_set_delay(JNIEnv *env, jclass clazz, jint ms)
{
return sSensorDevice->set_delay(sSensorDevice, ms);
}
static jint
android_data_wake(JNIEnv *env, jclass clazz)
{
int res = sSensorDevice->wake(sSensorDevice);
return res;
}
//提供给顶层实现的访问函数
static JNINativeMethod gMethods[] = {
{"_sensors_control_init", "()I", (void*) android_init },
{"_sensors_control_open", "()Landroid/os/Bundle;", (void*) android_open },
{"_sensors_control_close", "()I", (void*) android_close },
{"_sensors_control_activate", "(IZ)Z", (void*) android_activate },
{"_sensors_control_wake", "()I", (void*) android_data_wake },
{"_sensors_control_set_delay","(I)I", (void*) android_set_delay },
};
int register_android_server_SensorService(JNIEnv *env)
{
jclass clazz;
clazz = env->FindClass("java/io/FileDescriptor");
gFileDescriptorOffsets.mClass = (jclass)env->NewGlobalRef(clazz);
gFileDescriptorOffsets.mConstructor = env->GetMethodID(clazz, "<init>", "()V");
gFileDescriptorOffsets.mDescriptor = env->GetFieldID(clazz, "descriptor", "I");
clazz = env->FindClass("android/os/ParcelFileDescriptor");
gParcelFileDescriptorOffsets.mClass = (jclass) env->NewGlobalRef(clazz);
gParcelFileDescriptorOffsets.mConstructor = env->GetMethodID(clazz, "<init>",
"(Ljava/io/FileDescriptor;)V");
clazz = env->FindClass("android/os/Bundle");
gBundleOffsets.mClass = (jclass) env->NewGlobalRef(clazz);
gBundleOffsets.mConstructor = env->GetMethodID(clazz, "<init>", "()V");
gBundleOffsets.mPutIntArray = env->GetMethodID(clazz, "putIntArray", "(Ljava/lang/String;[I)V");
gBundleOffsets.mPutParcelableArray = env->GetMethodID(clazz, "putParcelableArray",
"(Ljava/lang/String;[Landroid/os/Parcelable;)V");
return jniRegisterNativeMethods(env, "com/android/server/SensorService",
gMethods, NELEM(gMethods));
}
}; // namespace android
到了这里文件系统中底层的部分算是完成了。对于下层类库而言,我们可以通过HAL的方式建立android Api 和硬件设备驱动连接的桥梁。针对不同的硬件平台需要用户自己编写这几个函数的实现方式并通过android kernel里的驱动(当然有部分驱动可能位于文件系统中)来控制硬件行为。对于上层而言,可以看做是给顶层Java实现Android API提供一个访问接口。由于该文件是编译成系统 的*.so库文件,这与NDK中的为系统加载一个*.so相似。
3、Application Framework层是如何监听Sensor的物理数据
到了这里,用NDK的编程经验,我们可以通过system.load("*.so")获得某个库的访问,并使用里面的函数,进行想要的操作。而Google为了方便用户操作,用Java语言提供一些便捷访问的Application Framework将底层的c/c++实现的驱动或者其他细节封装起来,也就是我们所说的API(\(^o^)/~个人理解)。
终于到了熟悉的Android API 层了。
对于重力感应器,Android在./framework/base/service/java/com/android/server/SensorsService.java
import android.content.Context;
import android.hardware.ISensorService;
import android.os.Binder;
import android.os.Bundle;
import android.os.RemoteException;
import android.os.IBinder;
import android.util.Config;
import android.util.Slog;
import android.util.PrintWriterPrinter;
import android.util.Printer;
import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.util.ArrayList;
import com.android.internal.app.IBatteryStats;
import com.android.server.am.BatteryStatsService;
/**
* Class that manages the device's sensors. It register clients and activate
* the needed sensors. The sensor events themselves are not broadcasted from
* this service, instead, a file descriptor is provided to each client they
* can read events from.
*/
class SensorService extends ISensorService.Stub {
static final String TAG = SensorService.class.getSimpleName();
private static final boolean DEBUG = false;
private static final boolean localLOGV = DEBUG ? Config.LOGD : Config.LOGV;
private static final int SENSOR_DISABLE = -1;
private int mCurrentDelay = 0;
/**
* Battery statistics to be updated when sensors are enabled and disabled.
*/
final IBatteryStats mBatteryStats = BatteryStatsService.getService();
private final class Listener implements IBinder.DeathRecipient {
final IBinder mToken;
final int mUid;
int mSensors = 0;
int mDelay = 0x7FFFFFFF;
Listener(IBinder token, int uid) {
mToken = token;
mUid = uid;
}
void addSensor(int sensor, int delay) {
mSensors |= (1<<sensor);
if (delay < mDelay)
mDelay = delay;
}
void removeSensor(int sensor) {
mSensors &= ~(1<<sensor);
}
boolean hasSensor(int sensor) {
return ((mSensors & (1<<sensor)) != 0);
}
public void binderDied() {
if (localLOGV) Slog.d(TAG, "sensor listener died");
synchronized(mListeners) {
mListeners.remove(this);
mToken.unlinkToDeath(this, 0);
// go through the lists of sensors used by the listener that
// died and deactivate them.
for (int sensor=0 ; sensor<32 && mSensors!=0 ; sensor++) {
if (hasSensor(sensor)) {
removeSensor(sensor);
deactivateIfUnusedLocked(sensor);
try {
mBatteryStats.noteStopSensor(mUid, sensor);
} catch (RemoteException e) {
// oops. not a big deal.
}
}
}
if (mListeners.size() == 0) {
_sensors_control_wake();
_sensors_control_close();
} else {
// TODO: we should recalculate the delay, since removing
// a listener may increase the overall rate.
}
mListeners.notify();
}
}
}
@SuppressWarnings("unused")
public SensorService(Context context) {
if (localLOGV) Slog.d(TAG, "SensorService startup");
_sensors_control_init();
}
public Bundle getDataChannel() throws RemoteException {
// synchronize so we do not require sensor HAL to be thread-safe.
synchronized(mListeners) {
return _sensors_control_open();
}
}
public boolean enableSensor(IBinder binder, String name, int sensor, int enable)
throws RemoteException {
if (localLOGV) Slog.d(TAG, "enableSensor " + name + "(#" + sensor + ") " + enable);
if (binder == null) {
Slog.e(TAG, "listener is null (sensor=" + name + ", id=" + sensor + ")");
return false;
}
if (enable < 0 && (enable != SENSOR_DISABLE)) {
Slog.e(TAG, "invalid enable parameter (enable=" + enable +
", sensor=" + name + ", id=" + sensor + ")");
return false;
}
boolean res;
int uid = Binder.getCallingUid();
synchronized(mListeners) {
res = enableSensorInternalLocked(binder, uid, name, sensor, enable);
if (res == true) {
// Inform battery statistics service of status change
long identity = Binder.clearCallingIdentity();
if (enable == SENSOR_DISABLE) {
mBatteryStats.noteStopSensor(uid, sensor);
} else {
mBatteryStats.noteStartSensor(uid, sensor);
}
Binder.restoreCallingIdentity(identity);
}
}
return res;
}
private boolean enableSensorInternalLocked(IBinder binder, int uid,
String name, int sensor, int enable) throws RemoteException {
// check if we have this listener
Listener l = null;
for (Listener listener : mListeners) {
if (binder == listener.mToken) {
l = listener;
break;
}
}
if (enable != SENSOR_DISABLE) {
// Activate the requested sensor
if (_sensors_control_activate(sensor, true) == false) {
Slog.w(TAG, "could not enable sensor " + sensor);
return false;
}
if (l == null) {
/*
* we don't have a listener for this binder yet, so
* create a new one and add it to the list.
*/
l = new Listener(binder, uid);
binder.linkToDeath(l, 0);
mListeners.add(l);
mListeners.notify();
}
// take note that this sensor is now used by this client
l.addSensor(sensor, enable);
} else {
if (l == null) {
/*
* This client isn't in the list, this usually happens
* when enabling the sensor failed, but the client
* didn't handle the error and later tries to shut that
* sensor off.
*/
Slog.w(TAG, "listener with binder " + binder +
", doesn't exist (sensor=" + name +
", id=" + sensor + ")");
return false;
}
// remove this sensor from this client
l.removeSensor(sensor);
// see if we need to deactivate this sensors=
deactivateIfUnusedLocked(sensor);
// if the listener doesn't have any more sensors active
// we can get rid of it
if (l.mSensors == 0) {
// we won't need this death notification anymore
binder.unlinkToDeath(l, 0);
// remove the listener from the list
mListeners.remove(l);
// and if the list is empty, turn off the whole sensor h/w
if (mListeners.size() == 0) {
_sensors_control_wake();
_sensors_control_close();
}
mListeners.notify();
}
}
// calculate and set the new delay
int minDelay = 0x7FFFFFFF;
for (Listener listener : mListeners) {
if (listener.mDelay < minDelay)
minDelay = listener.mDelay;
}
if (minDelay != 0x7FFFFFFF) {
mCurrentDelay = minDelay;
_sensors_control_set_delay(minDelay);
}
return true;
}
private void deactivateIfUnusedLocked(int sensor) {
int size = mListeners.size();
for (int i=0 ; i<size ; i++) {
if (mListeners.get(i).hasSensor(sensor)) {
// this sensor is still in use, don't turn it off
return;
}
}
if (_sensors_control_activate(sensor, false) == false) {
Slog.w(TAG, "could not disable sensor " + sensor);
}
}
@Override
protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
synchronized (mListeners) {
Printer pr = new PrintWriterPrinter(pw);
int c = 0;
pr.println(mListeners.size() + " listener(s), delay=" + mCurrentDelay + " ms");
for (Listener l : mListeners) {
pr.println("listener[" + c + "] " +
"sensors=0x" + Integer.toString(l.mSensors, 16) +
", uid=" + l.mUid +
", delay=" +
l.mDelay + " ms");
c++;
}
}
}
private ArrayList<Listener> mListeners = new ArrayList<Listener>();
//JNI提供的访问函数,将Application Framework和驱动衔接起来。
private static native int _sensors_control_init();
private static native Bundle _sensors_control_open();
private static native int _sensors_control_close();
private static native boolean _sensors_control_activate(int sensor, boolean activate);
private static native int _sensors_control_set_delay(int ms);
private static native int _sensors_control_wake();
}
接下来就是Core提供给我们使用的API了,这部分不在详解。
总结一下:对于NDK也不在啰唆,它和HAL一样,一个是提供商业保密程序,一个是google应厂商要求不希望公开源码。对于HAL的分析,我们也可以看到它大致的开发流程,也隐隐约约看到NDK的影子。若NDK涉及到硬件平台,它又该如何考虑夸平台呢?