Android實現FM收音機

實現源碼下載連接 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中啓動的FmServiceFmManager咱們在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

相關文章
相關標籤/搜索