實現源碼下載連接 http://download.csdn.net/detail/hi_zhengjian/8794731html
***方法一:直接在app裏面經過JNI訪問HAL層***java
FmRadio應用代碼結構以下:node
1、FMRadio.java經過FmRadioService提供的服務經過JNI訪問到HAL層:android
FmRadioService實現IfmService的一些接口,以及提供一些供Client調用的接口,接口裏面調用到JNI。c++
例如:express
FmRadioService.java中private voidenableFmFacility(boolean bEnable)調用到apache
enableFmFacility_native。api
enableFmFacility_native在com_android_server_FmService.cpp中實現,在映射表中註冊,也就是所謂的JNI. 最終調用com_android_server_FmService.cpp中的android_FmService_enableFmFacility。app
2、JNI簡介:less
傳統的JNI須要遵循必定的命名規則,而Android改變了這種方式,能夠經過如下兩種方法把java層與C++層的代碼進行映射,以下:
第一個參數是Java層的函數,第二個參數是函數的參數和返回類型也是屬於java層,第三個參數是JNI實現的c++函數
static const JNINativeMethodgMethods[] = {
/* name, signature, funcPtr */
{ "enableFmFacility_native", "(Z)I", (void*)android_FmService_enableFmFacility },
{ "setFmCurrentFreq_native", "(II)I", (void*)android_FmService_setFmCurrentFreq },
{ "stationIsAvailable_native", "()Z", (void*)android_FmService_stationIsAvailable },
{ "fm_mute_native", "(I)I", (void*)android_FmService_fm_mute },
};
下面這個函數是將全部的映射函數進行註冊
static int registerMethods(JNIEnv*env) {
static const char* const kClassName =
"com/rk/FmRadio/FmRadioService";
jclass clazz;
/* look up the class */
clazz = env->FindClass(kClassName);
if (clazz == NULL) {
return -1;
}
/* register all the methods */
if(env->RegisterNatives(clazz,gMethods,
sizeof(gMethods) /sizeof(gMethods[0])) != JNI_OK)
{
return -1;
}
/* fill out the rest of the ID cache */// .! :在 FM 實現中, 沒有必要 cache Java 類 or field 的 ID.
return cacheIds(env,clazz);
}
而後須要重寫JNI_OnLoad函數,這個函數必定要重載,當調用System.loadLibrary("rockchip_radio_jni");的時候,JVM啓動的時候就會自動加載,並將咱們的函數註冊到系統JNI,以便調用。
jintJNI_OnLoad(JavaVM* vm, void* reserved) {
JNIEnv* env = NULL;
jint result = -1;
if (vm->GetEnv((void**) &env,JNI_VERSION_1_4) != JNI_OK) {
goto bail;
}
assert(env != NULL);
if(registerMethods(env) != 0) {
goto bail;
}
/* success -- return valid version number */
result = JNI_VERSION_1_4;
bail:
return result;
}
或者若是在framework層也能夠在framework/base/services/jni/onload.app文件中直接在下面這個方法裏面添加咱們的註冊函數,這樣也能將咱們本身的函數列表註冊到系統JNI裏面。
JNI能夠參考文檔http://blog.csdn.net/zhenyongyuan123/article/details/5862054
最後特別須要注意的是,JNI裏面註冊的函數列表,在Java除了聲明爲native函數之外,並且在service裏面必需要調用到,不然會出現找不到該函數。
註冊好JNI以後,啓動service進行訪問,要注意的是系統app在Android4.2之後不能直接經過傳統的startservice(intent)啓動,這樣會出現權限問題,由於4.2之後Android引入多用戶操做,須要以下方式啓動
startServiceAsUser(mServiceIntent,newUserHandle(UserHandle.USER_CURRENT));
參考文檔http://xxhalbert.blog.163.com/blog/static/4849663420138127178898/
3、android_FmService_enableFmFacility會調用loadFmHal(&sFmControlDevice)
接口,loadFmHal(&sFmControlDevice)中會調用到\hardware\rk28\fm\fm.c的fm_control_open(&module->common,controlDevice),從而進入HAL層。
fm_control_open(&module->common,controlDevice)定義以下:
inline intfm_control_open(const struct hw_module_t* module, fm_control_device_t** device)
{
return module->methods->open(module,
FM_HARDWARE_CONTROL,
(structhw_device_t**)device);
}
========================================================================================================================================
相似的,其餘從上層的調用也會最終調用到fm.c的其餘函數,例如:
static boolean control_context__get_fm_stationIsAvailable(fm_control_device_t*dev)
{
int result = 0;
fm_control_context_t* this =(fm_control_context_t*)dev;
/* 若用戶已經禁用了 fm 機構. */
if ( !(this->is_fm_facility_enabled ) )
{
E("fm facility is totallydisabled.");
result = -1;
goto EXIT;
}
result =get_fm_stationIsAvailable();
EXIT:
return result;
}
=====================================================================================================================================
int get_fm_stationIsAvailable()
{
int ret;
int state;
if(fm_fd < 0)
{
E("no init \n");
return -1;
}
ret =ioctl(fm_fd,FM_STATION_ISAVAILABLE,&state);//對於Kernel,需實現這一類調用對應的驅動
if(ret < 0)
{
E("FM GET stationIsAvailable err,%s\n", strerror(errno));
return -1;
}
return state;
}
========================================================================================================================================
Android.mk中也要設置相應的值(黑體字):
#
# Copyright (C) 2008 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 "ASIS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express orimplied.
# See the License for the specific language governing permissionsand
# limitations under the License.
#
# This makefile supplies the rules for building a library of JNIcode for
# use by our example platform shared library.
LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)
LOCAL_LDLIBS := -llog
LOCAL_MODULE_TAGS := eng
# This is the target being built.
LOCAL_MODULE:=librockchip_radio_jni
# All of the source files that we will compile.
LOCAL_SRC_FILES:= \
com_android_server_FmService.cpp
# All of the shared libraries we link against.
LOCAL_SHARED_LIBRARIES := \
libandroid_runtime \
libnativehelper \
#libdwaharre \
libcutils \
libutils \
liblog \
libhardware
# No static libraries.
LOCAL_STATIC_LIBRARIES :=
# Also need the JNI headers.
LOCAL_C_INCLUDES += \
$(JNI_H_INCLUDE)
# No specia compiler flags.
LOCAL_CFLAGS =
# Don't prelink this library. For more efficient code, you may want
# to add this library to the prelink map and set this to true.
LOCAL_PRELINK_MODULE := false
include $(BUILD_SHARED_LIBRARY)
***方法二,將服務和JNI調用寫在framework裏面***
1、應用層:
代碼packages\apps\FmRadio
FmRadioService中會有如此調用:
private FmManagermManager;
mManager =(FmManager)FmRadioService.this.getSystemService(FM_SERVICE);
mManager.setFmCurrentFreq(currentFreq,direct);
經過FmManager咱們能夠調用到在Framework中啓動的FmService。FmManager咱們在framewok層中實現,而且在core/java/android/app/ContextImpl.java中註冊,以便應用層能夠像上面那樣獲取服務。
2、Framework層:
代碼路徑frameworks/base
修改的文件以下:
api/current.txt
core/java/android/app/ContextImpl.java
core/java/android/content/Context.java
core/java/android/view/KeyEvent.java
core/jni/AndroidRuntime.cpp
core/res/res/values/attrs.xml
services/java/com/android/server/SystemServer.java
services/jni/onload.cpp
新增的文件
core/java/android/os/FmManager.java
core/java/android/os/IFmManager.aidl
services/java/com/android/server/fm/FmService.java
services/jni/com_android_server_fm_FmService.cpp
實現開機時啓動FmService,FmService隨SystemServe啓動,
FmService 在 system 進程中, 具體實現 IFmManager 接口,FmServic實現的接口會經過JNI調用到com_android_server_fm_FmService.cpp實現的內容,JNI的內容會編譯進system/lib/libandroid_servers.so以供調用。
FmService.java以下:
core/java/android/os/IFmManager.aidl以下
能夠參考下面的網站來加,android 平臺添系統服務:
http://www.myexception.cn/android/1344211.html
http://www.360doc.com/content/12/0719/19/87000_225297295.shtml
3、HAL層代碼以下
/hardware/rk28/fm
Android.mk
fm.c
Log.h
rk28_fm.c
rk28_fm.h
Hal層是編譯/hardware/rk29/fm成/system/lib/hw/fm.rk2928board.so,這個命名是有講究的,隨意命名可能會致使加載失敗, hardware\rk29\fm\Android.mk中LOCAL_MODULE:= fm.$(TARGET_BOARD_HARDWARE), JNI中的com_android_server_fm_FmService.cpp中會調用hw_get_module來加載fm.rk2928board.so,以下:
static int loadFmHal(fm_control_device_t**controlDevice)
{
int result = 0;
hw_module_t const* module;
D("Load FM HAL module and open controldevice.");
/* 嘗試加載 HDMI HAL module的實例數據. */
if ( (result =hw_get_module(FM_HARDWARE_MODULE_ID, &module) ) != 0 )
{
E("Fail to load .so file of FM HALmodule. result = %d", result);
goto EXIT;
}
/* 獲取 控制設備的實例. */
{
if ( (result = fm_control_open(module,controlDevice) != 0 ) )
E("Fail to open control device ofFM HAL module. result = %d", result);
goto EXIT;
}
hardware\libhardware\hardware.c中對.so進行篩選,而後調用load來加載
int (const char *id, const struct hw_module_t **module)
{
D("hw_get_module");
return hw_get_module_by_class(id, NULL,module);
}
int hw_get_module_by_class(constchar *class_id, const char *inst,
const structhw_module_t **module)
{
int status;
int i;
const struct hw_module_t *hmi = NULL;
char prop[PATH_MAX];
char path[PATH_MAX];
char name[PATH_MAX];
//D("1111111");
snprintf(name, PATH_MAX,"%s.%s", class_id, inst);
//D("22222222222");
if (inst)
snprintf(name, PATH_MAX,"%s.%s", class_id, inst);
else
strlcpy(name, class_id, PATH_MAX);
//D("44444444");
/*
*Here we rely on the fact that calling dlopen multiple times on
* the same .so will simply increment arefcount (and not load
* a new copy of the library).
* We also assume that dlopen() isthread-safe.
*/
/* Loop through the configuration variantslooking for a module */
for (i=0 ; i<HAL_VARIANT_KEYS_COUNT+1 ;i++) {
//D("55555");
if (i < HAL_VARIANT_KEYS_COUNT) {
//D("66666");
if (property_get(variant_keys[i],prop, NULL) == 0) {
continue;
}
snprintf(path, sizeof(path),"%s/%s.%s.so",
HAL_LIBRARY_PATH2, name,prop);
if (access(path, R_OK) == 0) break;
snprintf(path, sizeof(path),"%s/%s.%s.so",
HAL_LIBRARY_PATH1, name,prop);
if (access(path, R_OK) == 0) break;
} else {
//D("7777");
snprintf(path, sizeof(path),"%s/%s.default.so",
HAL_LIBRARY_PATH2, name);
if (access(path, R_OK) == 0) break;
snprintf(path, sizeof(path),"%s/%s.default.so",
HAL_LIBRARY_PATH1, name);
if (access(path, R_OK) == 0) break;
}
}
//D("8888");
status = -ENOENT;
if (i < HAL_VARIANT_KEYS_COUNT+1) {
//D("9999");
/* load the module, if this fails,we're doomed, and we should not try
* to load a different variant. */
status = load(class_id, path, module);
}
HAL層調試注意點:
(1)Rk28_fm.h (hardware\rk29\fm\primitive) 中定義文件節點路徑,和kernel一致, #define FM_DEV"/dev/RADIO_FM8035"
Fm8035.c (kernel\drivers\fm\qn8035) #define DRV_NAME"RADIO_FM8035"//"FM8035"
fm_fd = open(FM_DEV, O_RDWR);
if(fm_fd < 0){
E("open %s err,%s\n", FM_DEV, strerror(errno));
return -1;
}
調用到驅動中的fm8035.c的int fm8035_dev_open(struct inode*inode, struct file *filp)
{
struct fm8035_dev_s *dev =&fm8035_dev;
printk("-----------rtc fm8035_open !");
filp->private_data = dev;
return 0;
(2)
rk28_fm.c中ret = ioctl(fm_fd, FM_TR_FUN_STOP, 1);
調用到fm8035.c的int fm8035_dev_ioctl(struct inode*inode, unsigned int cmd, unsigned long arg)
參數個數要對應,不然會報inalid argument
(3)device\rockchip\rksdk\init.rc中加上權限chmod 0664 /dev/RADIO_FM8035 chown system system /dev/RADIO_FM8035這樣rk28_fm.c中int fm_init(sighandler_t fm_sighandler)執行 fm_fd = open(FM_DEV, O_RDWR);的時候纔不會報permission denies(權限問題)。
(4) 在device/rockchip/rksdk/device.mk中
這裏,加上fm.$(TARGET_BOARD_HARDWARE)
HAL層的瞭解能夠參考文章
http://blog.csdn.net/sfrysh/article/details/7931593