本人喜歡用代碼+偽代碼的方式寫筆記。文中的花括號可能是方便記錄而已。
如:
hw_get_module(LED_HARDWARE_MODULE_ID, (const hw_module_t**)&module)
{
問:怎么獲得模塊信息的?
答:hardware\libhardware\Hardware.c
...........
}
原創分析, 轉載請注明出處:http://www.cnblogs.com/langlang/
作者email: dayhappyhappy@163.com
第一部分:HAL層
hardware\led\include\Led.h
定義常見數據結構
struct led_module_t {
struct hw_module_t common;
};
struct led_control_device_t {
struct hw_device_t common; /* 表示硬件設備 */
/* 屬性 */
int fd;
/* 提供出來的方法 */
int (*set_on)( struct led_control_device_t *dev, int32_t led);
int (*set_off)( struct led_control_device_t *dev, int32_t led);
};
nstruct led_control_context_t {
struct led_control_device_t device;
};
hardware\led\led\Led.cpp 分析
static int led_device_open( const struct hw_module_t* module, const char* name,
struct hw_device_t** device)
{
struct led_control_device_t *dev;
/* 分配設備 */
dev = ( struct led_control_device_t *)malloc( sizeof(*dev));
memset(dev, 0, sizeof(*dev));
dev->common.tag = HARDWARE_DEVICE_TAG;
dev->common.version = 0;
dev->common.module = ( struct hw_module_t*)module; /* 設置是屬於哪個模塊 */
dev->common.close = led_device_close;
dev->set_on = led_on{
/* 自定義方法 */
int led_off( struct led_control_device_t *dev, int32_t led)
{
ioctl(g_fd, 0, led); // led on
}
}
dev->set_off = led_off; /* 自定義方法 */
*device = &dev->common;
/*
/dev/leds0 內核驅動的device_create創建的
假如打開設備時候發生:Hello Stub: failed to open /dev/leds0 -- Permission denied.
進入到system/core/rootdir目錄,里面有一個名為ueventd.rc文件,往里面添加一行:
/dev/hello 0666 root leds0
*/
g_fd = open( " /dev/leds0 ", 0);
return 0;
}
/* 模塊方法表 */
static struct hw_module_methods_t led_module_methods = {
open: led_device_open
};
/*
模塊信息
實例變量名必須為HAL_MODULE_INFO_SYM,
tag也必須為HARDWARE_MODULE_TAG,這是Android硬件抽象層規范規定的。
*/
extern " C " ① const struct led_module_t HAL_MODULE_INFO_SYM = {
common: {
tag: HARDWARE_MODULE_TAG,
version_major: 1,
version_minor: 0,
id: LED_HARDWARE_MODULE_ID,
name: " Sample LED Stub ",
author: " The Forlinx Open Source Project ",
methods: &led_module_methods, /* 設置方法 */
}
/* supporting APIs go here */
};
① extern " C " :C++編寫的代碼片段可能被使用在其它語言編寫的代碼中。不同語言編寫的代碼互相調用是困難的。
為了使它們遵守統一規則,可以使用extern指定一個編譯和連接規約。 extern " C "指令中的C,表示的一種編譯和連接規約,
而不是一種語言。C表示符合C語言的編譯和連接規約的任何語言。
第二層: JNI 層次編寫
frameworks\ base\services\forlinx_led_jni\LedService.cpp
struct led_control_device_t *sLedDevice = NULL; // 硬件設備的公告屬性和方法
// 方法描述
gMethods[] = {
{ " _init ", " ()Z ",( void *)forlinx_init {
jboolean forlinx_init(JNIEnv *env, jclass clazz)
{
led_module_t* module;
hw_get_module(LED_HARDWARE_MODULE_ID, ( const hw_module_t**)&module)
{
問:怎么獲得模塊信息的?
答:hardware\libhardware\Hardware.c
hw_get_module( const char *id, const struct hw_module_t **module)
{
char prop[PATH_MAX];
char path[PATH_MAX];
for (i= 0 ; i<HAL_VARIANT_KEYS_COUNT+ 1 ; i++)
{
property_get(variant_keys[i], prop, NULL)
{
問: variant_keys的數據是什么?
答: static const char *variant_keys[] = {
" ro.hardware ",
" ro.product.board ",
" ro.board.platform ",
" ro.arch "
};
問:ro.hardware代表的值是什么?
答: system\core\init\init.c
set_init_properties_action( int nargs, char **args)
property_set( " ro.hardware ", hardware)
get_hardware_name( char *hardware, unsigned int *revision)
{
// 該函數在int main(int argc, char **argv)中被調用
fd = open( " /proc/cpuinfo ", O_RDONLY);
hw = strstr(data, " \nHardware ");
while (*x && !isspace(*x)) {
hardware[n++] = tolower(*x);
x++;
if (n == 31) break;
}
}
}
snprintf(path, sizeof(path), " %s/%s.%s.so ",HAL_LIBRARY_PATH1, id, prop);
{
#define HAL_LIBRARY_PATH1 "/system/lib/hw"
}
}
status = load(id, path, module); /* 調用load函數打開動態鏈接庫 */
}
led_control_open(&module->common, &sLedDevice)
{
led_control_open( const struct hw_module_t* module, struct led_control_device_t** device) {
return module->methods->open(module,LED_HARDWARE_MODULE_ID, ( struct hw_device_t**)device);
}
}
}
}
}},
{ " _set_on ", " (I)Z ", ( void *)forlinx_setOn
{
sLedDevice->set_on(sLedDevice, led); // sLedDevice:硬件設備的公告屬性和方法
}
},
{ " _set_off ", " (I)Z ", ( void *)forlinx_setOff
{
sLedDevice->set_off(sLedDevice, led);
}
},
}
int register_forlinx_server_LedService(JNIEnv* env)
{
char* const kClassName = " forlinx_led_server/server/LedService ";
/* look up the class */
jclass clazz = env->FindClass(kClassName);
/* 注冊方法
java類: forlinx_led_server/server/LedService
方法描述: gMethods
*/
env->RegisterNatives(clazz, gMethods, sizeof(gMethods) / sizeof(gMethods[ 0])); // gMethods 方法描述數組
}
簡單的Jni 例子都是映射模式,及對應的Jni 的c/c++ 實現需要,
被java的函數命名規則限制死,為了解決這類毛病,引入的JNI_OnLoad這類方法。
jint JNI_OnLoad(JavaVM* vm, void* reserved)
該方法在Jni so 被加載時調用。
當VM釋放該組件時會呼叫JNI_OnUnload()函數
*/
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;
}
register_forlinx_server_LedService(env);
{
char* const kClassName = " forlinx_led_server/server/LedService ";
}
return JNI_VERSION_1_4;
}
第三層: Application Frameworks層增加硬件訪問服務
1 接口文件指定
frameworks\ base\Android.mk 指定接口文件
{
LOCAL_SRC_FILES += \
core/java/android/forlinx/ILedService.aidl \
}
2 接口定義 frameworks\ base\core\java\android\forlinx\ILedService.aidl
package android.forlinx;
interface ILedService
{
boolean setOn( int led);
boolean setOff( int led);
}
3 實現類
第一種方法:直接調用service方法的實現過程
import android.forlinx.ILedService;
public final class LedService extends ILedService.Stub {
// 先與構造函數執行
static
{
System.load( " /system/lib/libforlinx_runtime.so "); // 路徑是怎么確定的?
}
public LedService() {
_init();
}
public boolean setOn( int led)
{
return _set_on(led);
}
public boolean setOff( int led) {
return _set_off(led);
}
private static native boolean _init();
private static native boolean _set_on( int led);
private static native boolean _set_off( int led);
}
第二種方法:經過Manager調用service
為什么要這樣做?
LedManager代理者模式,LedSystemServer單例模式
( 1):添加服務 packages\apps\forlinxled\src\com\led\LedSystemServer.java
import forlinx_led_server.server.LedService;
public class LedSystemServer extends Service {
@Override
public IBinder onBind(Intent intent) { return null;}
public void onStart(Intent intent, int startId) {
if(ServiceManager.getService( " led ") == null) // 單例模式
{
LedService ls = new LedService();
ServiceManager.addService( " led ", ls);
}
}
}
由Manager充當代理 代理模式
frameworks\ base\core\java\android\forlinx\LedManager.java
public class LedManager
{
private static final String TAG = " LedManager ";
private ILedService mLedService;
public LedManager() {
mLedService = ILedService.Stub.asInterface(ServiceManager.getService( " led "));
}
public boolean LedOn( int n) {
boolean result = false;
if (mLedService == null) // try
{
mLedService = ILedService.Stub.asInterface(ServiceManager.getService( " led "));
}
if(mLedService != null)
{
result = mLedService.setOn(n);
}
return result;
}
public boolean LedOff( int n) {
boolean result = false;
try {
if (mLedService == null) // try
{
mLedService = ILedService.Stub.asInterface(
ServiceManager.getService( " led "));
}
if(mLedService != null)
{
Log.i(TAG, " The LedManager object will set off ");
result = mLedService.setOff(n);
}
} catch (RemoteException e) {
Log.e(TAG, " RemoteException in LedManager.LedOff: ", e);
}
return result;
}
}
4 測試程序 第一種方法:直接調用service方法的實現過程
import android.widget.TextView;
public class LedClient extends Activity {
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Call an API on the library.
LedService ls = new LedService();
ls.setOn( 0);
ls.setOn( 1);
ls.setOn( 2);
ls.setOn( 3);
TextView tv = new TextView( this);
tv.setText( " All Leds On ");
setContentView(tv);
}
}
第二種方法:經過Manager調用service。HAL、JNI兩層和第一種方法一樣。
public class LedTest extends Activity implements OnClickListener {
private LedManager mLedManager = null;
private Button btnLED1On;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
// Start LedService in a seperated process.
startService( new Intent( " com.led.systemserver "));
// Get LedManager.
if (mLedManager == null)
{
mLedManager = new LedManager();
}
btnLED1On = (Button)findViewById(R.id.btnLED1On);
btnLED1On.setOnClickListener( this);
}
public void onClick(View v) {
switch (v.getId()) {
case R.id.btnLED1On:
if (mLedManager != null)
{
mLedManager.LedOn( 0);
}
break;
default:
break;
}
}
}
hardware\led\include\Led.h
定義常見數據結構
struct led_module_t {
struct hw_module_t common;
};
struct led_control_device_t {
struct hw_device_t common; /* 表示硬件設備 */
/* 屬性 */
int fd;
/* 提供出來的方法 */
int (*set_on)( struct led_control_device_t *dev, int32_t led);
int (*set_off)( struct led_control_device_t *dev, int32_t led);
};
nstruct led_control_context_t {
struct led_control_device_t device;
};
hardware\led\led\Led.cpp 分析
static int led_device_open( const struct hw_module_t* module, const char* name,
struct hw_device_t** device)
{
struct led_control_device_t *dev;
/* 分配設備 */
dev = ( struct led_control_device_t *)malloc( sizeof(*dev));
memset(dev, 0, sizeof(*dev));
dev->common.tag = HARDWARE_DEVICE_TAG;
dev->common.version = 0;
dev->common.module = ( struct hw_module_t*)module; /* 設置是屬於哪個模塊 */
dev->common.close = led_device_close;
dev->set_on = led_on{
/* 自定義方法 */
int led_off( struct led_control_device_t *dev, int32_t led)
{
ioctl(g_fd, 0, led); // led on
}
}
dev->set_off = led_off; /* 自定義方法 */
*device = &dev->common;
/*
/dev/leds0 內核驅動的device_create創建的
假如打開設備時候發生:Hello Stub: failed to open /dev/leds0 -- Permission denied.
進入到system/core/rootdir目錄,里面有一個名為ueventd.rc文件,往里面添加一行:
/dev/hello 0666 root leds0
*/
g_fd = open( " /dev/leds0 ", 0);
return 0;
}
/* 模塊方法表 */
static struct hw_module_methods_t led_module_methods = {
open: led_device_open
};
/*
模塊信息
實例變量名必須為HAL_MODULE_INFO_SYM,
tag也必須為HARDWARE_MODULE_TAG,這是Android硬件抽象層規范規定的。
*/
extern " C " ① const struct led_module_t HAL_MODULE_INFO_SYM = {
common: {
tag: HARDWARE_MODULE_TAG,
version_major: 1,
version_minor: 0,
id: LED_HARDWARE_MODULE_ID,
name: " Sample LED Stub ",
author: " The Forlinx Open Source Project ",
methods: &led_module_methods, /* 設置方法 */
}
/* supporting APIs go here */
};
① extern " C " :C++編寫的代碼片段可能被使用在其它語言編寫的代碼中。不同語言編寫的代碼互相調用是困難的。
為了使它們遵守統一規則,可以使用extern指定一個編譯和連接規約。 extern " C "指令中的C,表示的一種編譯和連接規約,
而不是一種語言。C表示符合C語言的編譯和連接規約的任何語言。
第二層: JNI 層次編寫
frameworks\ base\services\forlinx_led_jni\LedService.cpp
struct led_control_device_t *sLedDevice = NULL; // 硬件設備的公告屬性和方法
// 方法描述
gMethods[] = {
{ " _init ", " ()Z ",( void *)forlinx_init {
jboolean forlinx_init(JNIEnv *env, jclass clazz)
{
led_module_t* module;
hw_get_module(LED_HARDWARE_MODULE_ID, ( const hw_module_t**)&module)
{
問:怎么獲得模塊信息的?
答:hardware\libhardware\Hardware.c
hw_get_module( const char *id, const struct hw_module_t **module)
{
char prop[PATH_MAX];
char path[PATH_MAX];
for (i= 0 ; i<HAL_VARIANT_KEYS_COUNT+ 1 ; i++)
{
property_get(variant_keys[i], prop, NULL)
{
問: variant_keys的數據是什么?
答: static const char *variant_keys[] = {
" ro.hardware ",
" ro.product.board ",
" ro.board.platform ",
" ro.arch "
};
問:ro.hardware代表的值是什么?
答: system\core\init\init.c
set_init_properties_action( int nargs, char **args)
property_set( " ro.hardware ", hardware)
get_hardware_name( char *hardware, unsigned int *revision)
{
// 該函數在int main(int argc, char **argv)中被調用
fd = open( " /proc/cpuinfo ", O_RDONLY);
hw = strstr(data, " \nHardware ");
while (*x && !isspace(*x)) {
hardware[n++] = tolower(*x);
x++;
if (n == 31) break;
}
}
}
snprintf(path, sizeof(path), " %s/%s.%s.so ",HAL_LIBRARY_PATH1, id, prop);
{
#define HAL_LIBRARY_PATH1 "/system/lib/hw"
}
}
status = load(id, path, module); /* 調用load函數打開動態鏈接庫 */
}
led_control_open(&module->common, &sLedDevice)
{
led_control_open( const struct hw_module_t* module, struct led_control_device_t** device) {
return module->methods->open(module,LED_HARDWARE_MODULE_ID, ( struct hw_device_t**)device);
}
}
}
}
}},
{ " _set_on ", " (I)Z ", ( void *)forlinx_setOn
{
sLedDevice->set_on(sLedDevice, led); // sLedDevice:硬件設備的公告屬性和方法
}
},
{ " _set_off ", " (I)Z ", ( void *)forlinx_setOff
{
sLedDevice->set_off(sLedDevice, led);
}
},
}
int register_forlinx_server_LedService(JNIEnv* env)
{
char* const kClassName = " forlinx_led_server/server/LedService ";
/* look up the class */
jclass clazz = env->FindClass(kClassName);
/* 注冊方法
java類: forlinx_led_server/server/LedService
方法描述: gMethods
*/
env->RegisterNatives(clazz, gMethods, sizeof(gMethods) / sizeof(gMethods[ 0])); // gMethods 方法描述數組
}
簡單的Jni 例子都是映射模式,及對應的Jni 的c/c++ 實現需要,
被java的函數命名規則限制死,為了解決這類毛病,引入的JNI_OnLoad這類方法。
jint JNI_OnLoad(JavaVM* vm, void* reserved)
該方法在Jni so 被加載時調用。
當VM釋放該組件時會呼叫JNI_OnUnload()函數
*/
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;
}
register_forlinx_server_LedService(env);
{
char* const kClassName = " forlinx_led_server/server/LedService ";
}
return JNI_VERSION_1_4;
}
第三層: Application Frameworks層增加硬件訪問服務
1 接口文件指定
frameworks\ base\Android.mk 指定接口文件
{
LOCAL_SRC_FILES += \
core/java/android/forlinx/ILedService.aidl \
}
2 接口定義 frameworks\ base\core\java\android\forlinx\ILedService.aidl
package android.forlinx;
interface ILedService
{
boolean setOn( int led);
boolean setOff( int led);
}
3 實現類
第一種方法:直接調用service方法的實現過程
import android.forlinx.ILedService;
public final class LedService extends ILedService.Stub {
// 先與構造函數執行
static
{
System.load( " /system/lib/libforlinx_runtime.so "); // 路徑是怎么確定的?
}
public LedService() {
_init();
}
public boolean setOn( int led)
{
return _set_on(led);
}
public boolean setOff( int led) {
return _set_off(led);
}
private static native boolean _init();
private static native boolean _set_on( int led);
private static native boolean _set_off( int led);
}
第二種方法:經過Manager調用service
為什么要這樣做?
LedManager代理者模式,LedSystemServer單例模式
( 1):添加服務 packages\apps\forlinxled\src\com\led\LedSystemServer.java
import forlinx_led_server.server.LedService;
public class LedSystemServer extends Service {
@Override
public IBinder onBind(Intent intent) { return null;}
public void onStart(Intent intent, int startId) {
if(ServiceManager.getService( " led ") == null) // 單例模式
{
LedService ls = new LedService();
ServiceManager.addService( " led ", ls);
}
}
}
由Manager充當代理 代理模式
frameworks\ base\core\java\android\forlinx\LedManager.java
public class LedManager
{
private static final String TAG = " LedManager ";
private ILedService mLedService;
public LedManager() {
mLedService = ILedService.Stub.asInterface(ServiceManager.getService( " led "));
}
public boolean LedOn( int n) {
boolean result = false;
if (mLedService == null) // try
{
mLedService = ILedService.Stub.asInterface(ServiceManager.getService( " led "));
}
if(mLedService != null)
{
result = mLedService.setOn(n);
}
return result;
}
public boolean LedOff( int n) {
boolean result = false;
try {
if (mLedService == null) // try
{
mLedService = ILedService.Stub.asInterface(
ServiceManager.getService( " led "));
}
if(mLedService != null)
{
Log.i(TAG, " The LedManager object will set off ");
result = mLedService.setOff(n);
}
} catch (RemoteException e) {
Log.e(TAG, " RemoteException in LedManager.LedOff: ", e);
}
return result;
}
}
4 測試程序 第一種方法:直接調用service方法的實現過程
import android.widget.TextView;
public class LedClient extends Activity {
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Call an API on the library.
LedService ls = new LedService();
ls.setOn( 0);
ls.setOn( 1);
ls.setOn( 2);
ls.setOn( 3);
TextView tv = new TextView( this);
tv.setText( " All Leds On ");
setContentView(tv);
}
}
第二種方法:經過Manager調用service。HAL、JNI兩層和第一種方法一樣。
public class LedTest extends Activity implements OnClickListener {
private LedManager mLedManager = null;
private Button btnLED1On;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
// Start LedService in a seperated process.
startService( new Intent( " com.led.systemserver "));
// Get LedManager.
if (mLedManager == null)
{
mLedManager = new LedManager();
}
btnLED1On = (Button)findViewById(R.id.btnLED1On);
btnLED1On.setOnClickListener( this);
}
public void onClick(View v) {
switch (v.getId()) {
case R.id.btnLED1On:
if (mLedManager != null)
{
mLedManager.LedOn( 0);
}
break;
default:
break;
}
}
}