Android個層次呼叫流程概述
簡單來說,就是對Linux核心驅動程式的封裝,向上提供介面,遮蔽低層的實現細節。
也就是說,把對硬體的支援分成了兩層,
一層放在使用者空間(User Space),(硬體抽象層)
一層放在核心空間(Kernel Space),(Linux核心驅動程式)
下面這個圖闡述了硬體抽象層在Android系統中的位置,以及它和其它層的關係:
二,簡單的總結
進入到Android原始碼工程的external目錄,建立hello目錄:
cd external
mkdir hello
在hello目錄中新建Android.mk檔案:
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE_TAGS := optional
LOCAL_MODULE := hello
LOCAL_SRC_FILES := $(call all-subdir-c-files)
include $(BUILD_EXECUTABLE)
注意,BUILD_EXECUTABLE表示我們要編譯的是可執行程式。
使用mmm命令進行編譯:
mmm ./external/hello 編譯成功後,就可以在out/target/product/gerneric/system/bin目錄下,看到可執行檔案hello了。
重新打包Android系統檔案system.img:
make snod
這樣,重新打包後的system.img檔案就包含剛才編譯好的hello可執行檔案了。
七. 執行模擬器,使用/system/bin/hello可執行程式來訪問Linux核心驅動程式。
emulator -kernel ./kernel/common/arch/arm/boot/zImage &
adb shell
cd system/bin
./hello
三,編寫硬體抽象層
進入到在hardware/libhardware/include/hardware目錄,新建hello.h檔案: cd hardware/libhardware/include/hardware vi hello.h hello.h檔案的內容如下:
ifndef ANDROID_HELLO_INTERFACE_H
define ANDROID_HELLO_INTERFACE_H
include
__BEGIN_DECLS
/定義模組ID/
define HELLO_HARDWARE_MODULE_ID “hello”
/硬體模組結構體/
struct hello_module_t {
struct hw_module_t common;
};
/硬體介面結構體/
struct hello_device_t {
struct hw_device_t common;
int fd; // 裝置檔案描述符
int (*set_val)(struct hello_device_t* dev, int val); // 為該HAL對上提供的函式介面
int (*get_val)(struct hello_device_t* dev, int* val);
};
__END_DECLS
endif
進入到hardware/libhardware/modules目錄,新建hello目錄,並新增hello.c檔案。 hello.c的內容較多,我們分段來看。
首先是包含相關標頭檔案和定義相關結構:
define LOG_TAG “HelloStub”
include
include
include
include
include
include
define DEVICE_NAME “/dev/hello”
define MODULE_NAME “Hello”
/裝置開啟和關閉介面/
static int hello_device_open(const struct hw_module_t* module, const char* name, struct hw_device_t** device);
static int hello_device_close(struct hw_device_t* device);
/裝置訪問介面/
static int hello_set_val(struct hello_device_t* dev, int val);
static int hello_get_val(struct hello_device_t* dev, int* val);
/模組方法表/
static struct hw_module_methods_t hello_module_methods = {
open: hello_device_open
};
/模組例項變數/
struct hello_module_t HAL_MODULE_INFO_SYM = {
common: {
tag: HARDWARE_MODULE_TAG,
version_major: 1,
version_minor: 0,
id: HELLO_HARDWARE_MODULE_ID,
name: MODULE_NAME,
author: MODULE_AUTHOR,
methods: &hello_module_methods,
}
};
include “jni.h”
include “JNIHelp.h”
include “android_runtime/AndroidRuntime.h”
include
include
include
include
include
/* 接著定義hello_init、hello_getVal和hello_setVal三個JNI方法:*/
namespace android
{
/*在硬體抽象層中定義的硬體訪問結構體,參考*/
struct hello_device_t* hello_device = NULL;
/*通過硬體抽象層定義的硬體訪問介面設定硬體暫存器val的值*/
static void hello_setVal (JNIEnv* env, jobject clazz, jint value) {
int val = value;
LOGI("Hello JNI: set value %d to device.", val);
if(!hello_device) {
LOGI("Hello JNI: device is not open.");
return;
}
hello_device->set_val(hello_device, val); // 在抽象層的open中定義
}
/*通過硬體抽象層定義的硬體訪問介面讀取硬體暫存器val的值*/
static jint hello_getVal(JNIEnv* env, jobject clazz) {
int val = 0;
if(!hello_device) {
LOGI("Hello JNI: device is not open.");
return val;
}
hello_device->get_val(hello_device, &val);
LOGI("Hello JNI: get value %d from device.", val);
return val;
}
/*通過硬體抽象層定義的硬體模組開啟介面 開啟硬體裝置*/
static inline int hello_device_open (const hw_module_t* module, struct hello_device_t** device) {
return module->methods->open(module, HELLO_HARDWARE_MODULE_ID, (struct hw_device_t**)device);
}
/*通過硬體模組ID來載入指定的硬體抽象層模組並開啟硬體*/
static jboolean hello_init (JNIEnv* env, jclass clazz) {
hello_module_t* module;
LOGI("Hello JNI: initializing......");
/* 載入模組ID為HELLO_HARDWARE_MODULE_ID的硬體抽象層模組, Android硬體抽象層會根據HELLO_HARDWARE_MODULE_ID的值
* 在Android系統的/system/lib/hw目錄中找到相應的模組,然後載入起來,並且返回hw_module_t介面給呼叫者使用。
*/
if ( hw_get_module ( HELLO_HARDWARE_MODULE_ID, (const struct hw_module_t**)&module ) == 0 ) {
LOGI("Hello JNI: hello Stub found.");
if(hello_device_open(&(module->common), &hello_device) == 0) {
LOGI("Hello JNI: hello device is open.");
return 0;
}
LOGE("Hello JNI: failed to open hello device.");
return -1;
}
LOGE("Hello JNI: failed to get hello stub module.");
return -1;
}
/*JNI方法表*/
static const JNINativeMethod method_table[ ] = {
{"init_native", "()Z", (void*)hello_init},
{"setVal_native", "(I)V", (void*)hello_setVal},
{"getVal_native", "()I", (void*)hello_getVal},
};
/*註冊JNI方法*/
int register_android_server_HelloService(JNIEnv *env) {
return jniRegisterNativeMethods(env, "com/android/server/HelloService", method_table, NELEM(method_table) );
// 必須對應HelloService所在的包的路徑
}
};
在Android系統初始化時,使其自動載入該JNI方法呼叫表
修改同目錄下的onload.cpp檔案,
(1)在namespace android增加 register_android_server_HelloService函式宣告:
namespace android {
..............................................................................................
int register_android_server_HelloService(JNIEnv *env);
};
(2)在JNI_onLoad 增加 register_android_server_HelloService函式呼叫:
extern "C" jint JNI_onLoad(JavaVM* vm, void* reserved)
{
.................................................................................................
register_android_server_HelloService(env);
.................................................................................................
}
(3)修改同目錄下的Android.mk檔案,在 LOCAL_SRC_FILES變數 中增加一行:
LOCAL_SRC_FILES:= \
com_android_server_AlarmManagerService.cpp \
com_android_server_BatteryService.cpp \
com_android_server_InputManager.cpp \
com_android_server_LightsService.cpp \
com_android_server_PowerManagerService.cpp \
com_android_server_SystemServer.cpp \
com_android_server_UsbService.cpp \
com_android_server_VibratorService.cpp \
com_android_server_location_GpsLocationProvider.cpp \
com_android_server_HelloService.cpp /
onload.cpp
最後編譯
mmm frameworks/base/services/jni
make snod
這樣,重新打包的system.img映象檔案就包含我們剛才編寫的JNI方法了,
我們可以通過Android系統的Application Frameworks層提供的硬體服務HelloService來呼叫這些JNI方法,進而呼叫低層的硬體抽象層介面去訪問硬體了。
五:提供Java訪問硬體服務介面
Linux核心層、硬體抽象層和執行時庫層提供的自定義硬體服務介面,這些介面都是通過C或者C++語言來實現的。
以下,我們將介紹如何在Android系統的Application Frameworks層提供Java介面的硬體服務。
(為什麼用代理?)
在Android系統中,硬體服務一般是執行在一個獨立的程序中為各種應用程式提供服務。因此,呼叫這些硬體服務的應用程式與這些硬體服務之間的通訊需要通過代理來進行。為此,我們要先定義好通訊介面。
(1)進入到frameworks/base/core/java/android/os目錄,新增IHelloService.aidl介面定義檔案:
cd frameworks/base/core/java/android/os
vi IHelloService.aidl
IHelloService.aidl定義了IHelloService介面:
package android.os;
interface IHelloService{
void setVal (int val);
int getVal ( );
}
該介面主要提供了裝置獲取硬體暫存器val的值的功能,分別通過setVal和getVal兩個函式來實現。
(2)返回到frameworks/base目錄,開啟Android.mk檔案,修改LOCAL_SRC_FILES變數的值,增加IHelloService.aidl原始檔:
core/java/android/os/IHelloService.aidl /
(3)編譯IHelloService.aidl介面:
mmm frameworks/base
這樣,就會根據IHelloService.aidl生成相應的IHelloService.Stub介面。
(4)進入到frameworks/base/services/java/com/android/server目錄,新增HelloService.java檔案:
package com.android.server;
import android.content.Context;
import android.os.IHelloService;
import android.util.Slog;
public class HelloService extends IHelloService . Stub {
private static final String TAG = "HelloService";
HelloService() {
init_native();
}
public void setVal(int val) {
setVal_native(val);
}
public int getVal() {
return getVal_native();
}
/* HelloService主要是通過呼叫JNI方法init_native、setVal_native和getVal_native來提供硬體服務 */
private static native boolean init_native();
private static native void setVal_native(int val);
private static native int getVal_native();
};
(5) 修改同目錄的SystemServer.java檔案,在ServerThread::run函式中增加載入HelloService的程式碼:
@Override
public void run() {
....................................................................................
try {
Slog.i(TAG, "Hello Service");
ServiceManager. addService("hello", new HelloService());
} catch (Throwable e) {
Slog.e(TAG, "Failure starting Hello Service", e);
}
......................................................................................
}
(6)編譯HelloService和重新打包system.img:
mmm frameworks/base/services/java
make snod
這樣,重新打包後的system.img系統映象檔案就在Application Frameworks層中包含了我們自定義的硬體服務HelloService了,並且會在系統啟動的時候,自動載入HelloService。
這時,應用程式就可以通過Java介面來訪問Hello硬體服務了。
六:Java呼叫例項
public class Hello extends Activityimplements OnClickListener {
private final static String LOG_TAG =”shy.luo.renju.Hello”;
private IHelloService helloService = null;
private EditText valueText = null;
private Button readButton = null;
private Button writeButton = null;
private Button clearButton = null;
/* Called when the activity is first created. /
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
// IHelloService介面定義在android.os.IHelloService中,
helloService= IHelloService.Stub.asInterface( // 轉換為IHelloService介面
ServiceManager.getService("hello")); // 獲得HelloService
// 服務名字“hello”是系統啟動時載入HelloService時指定
valueText = (EditText)findViewById(R.id.edit_value);
readButton = (Button)findViewById(R.id.button_read);
writeButton = (Button)findViewById(R.id.button_write);
clearButton = (Button)findViewById(R.id.button_clear);
readButton.setOnClickListener(this);
writeButton.setOnClickListener(this);
clearButton.setOnClickListener(this);
Log.i(LOG_TAG, "Hello Activity Created");
}
@Override
public void onClick(View v) {
if(v.equals(readButton)) {
try {
int val =helloService.getVal();
String text = String.valueOf(val);
valueText.setText(text);
} catch (RemoteException e) {
Log.e(LOG_TAG, "RemoteException while reading value from device.");
}
}
else if(v.equals(writeButton)) {
try {
String text =valueText.getText().toString();
int val =Integer.parseInt(text);
helloService.setVal(val);
} catch (RemoteException e) {
Log.e(LOG_TAG, "RemoteException while writing value to device.");
}
}
else if(v.equals(clearButton)) {
String text = "";
valueText.setText(text);
}
}
}