参考不同平台下FMRadio App中jni库的实现,本文是基于MTK平台实现,所以参考vendor\mediatek\proprietary\packages\apps\FMRadio\jni\fmr
.
├── Android.mk
├── common.cpp
├── fm.h
├── fmr_core.cpp
├── fmr_err.cpp
├── fmr.h
├── libfm_jni.cpp
├── NOTICE
└── README
最先应该关注的是,底层驱动控制FM的方式,当然我们会想到最简单粗暴的形式是,直接操作所有功能节点,实际情况是做了封装,以ioctl命令去执行我们熟悉的powerup、powerdown、tune、seek、mute等等.
fmr.h,需要直接关注下其中定义好的ioctl命令,此处也需要一个前提,即是kernel必须实现了这些供调用
// ********** **FM IOCTL define start *******************************
#define FM_IOC_MAGIC 0xf5
#define FM_IOCTL_POWERUP _IOWR(FM_IOC_MAGIC, 0, struct fm_tune_parm)
#define FM_IOCTL_POWERDOWN _IOWR(FM_IOC_MAGIC, 1, int32_t)
#define FM_IOCTL_TUNE _IOWR(FM_IOC_MAGIC, 2, struct fm_tune_parm)
#define FM_IOCTL_SEEK _IOWR(FM_IOC_MAGIC, 3, struct fm_seek_parm)
#define FM_IOCTL_SETVOL _IOWR(FM_IOC_MAGIC, 4, uint32_t)
#define FM_IOCTL_GETVOL _IOWR(FM_IOC_MAGIC, 5, uint32_t)
#define FM_IOCTL_MUTE _IOWR(FM_IOC_MAGIC, 6, uint32_t)
#define FM_IOCTL_GETRSSI _IOWR(FM_IOC_MAGIC, 7, int32_t)
#define FM_IOCTL_SCAN _IOWR(FM_IOC_MAGIC, 8, struct fm_scan_parm)
#define FM_IOCTL_STOP_SCAN _IO(FM_IOC_MAGIC, 9)
…
so为独立的,方便与内置的FM做模块兼容,基本的代码调用
代码结构如下:
.
├── Android.mk
├── common.cpp
├── ext_fm.cpp
├── fm.h
├── fmr_core.cpp
└── fmr.h
common.cpp直接封装底层ioctl的接口
结构体fm_cbk_tbl定义需要封装的所有ioctl命令的函数
struct fm_cbk_tbl {
//Basic functions.
int (*open_dev)(const char *pname, int *fd);
int (*close_dev)(int fd);
int (*pwr_up)(int fd, int band, int freq);
int (*pwr_down)(int fd, int type);
int (*seek)(int fd, int *freq, int band, int dir, int lev);
...
}
入口函数,指针指向
void FM_interface_init(struct fm_cbk_tbl *cbk_tbl)
{
//Basic functions.
cbk_tbl->open_dev = COM_open_dev_ext;
cbk_tbl->close_dev = COM_close_dev_ext;
cbk_tbl->pwr_up = COM_pwr_up_ext;
cbk_tbl->pwr_down = COM_pwr_down_ext;
cbk_tbl->tune = COM_tune_ext;
cbk_tbl->set_mute = COM_set_mute_ext;
cbk_tbl->seek_all_ext = COM_Seek_all_ext_fm;
...
return;
}
单个函数具体实现,以上电为例,FM_IOCTL_POWERUP即是底层已经实现具体功能的ioctl
int COM_pwr_up_ext(int fd, int band, int freq)
{
fm_tune_parm parm;
int ret = 0;
int pwrState = -1;
parm.band = band;
parm.freq = freq;
ret = ioctl(fd, FM_IOCTL_POWERUP, &parm);
LOGE("%s, ExtFm Externel FM power up \n", __func__);
if(ret < 0){
LOGE("%s, ExtFm Externel FM power up failed\n", __func__);
}
return ret;
}
fmr_core.cpp进一步封装,供jni调用
int FMR_pwr_up(int idx, int freq)
{
int ret = 0;
FMR_ASSERT(FMR_cbk_tbl(idx).pwr_up);
LOGI("%s,[freq=%d]\n", __func__, freq);
if (freq < fmr_data.cfg_data.low_band || freq > fmr_data.cfg_data.high_band) {
LOGE("%s error freq: %d\n", __func__, freq);
ret = -ERR_INVALID_PARA;
return ret;
}
ret = FMR_cbk_tbl(idx).pwr_up(FMR_fd(idx), fmr_data.cfg_data.band, freq);
if (ret < 1) {
LOGE("%s failed, [ret=%d]\n", __func__, ret);
}
fmr_data.cur_freq = freq;
LOGD("%s, [ret=%d]\n", __func__, ret);
return ret;
}
ext_fm.cpp jni函数的注册,以提供静态接口给上层app java调用
jboolean powerUp(JNIEnv *env, jobject thiz, jfloat freq)
{
int ret = 0;
int tmp_freq;
UNUSED(env);
UNUSED(thiz);
LOGI("%s, [freq=%d]\n", __func__, (int)freq);
tmp_freq = (int)(freq * 100); //Eg, 87.5 * 100 --> 8750
ret = FMR_pwr_up(g_idx, tmp_freq);
LOGD(" ExtFm [ret=%d]\n", ret);
return ret?JNI_FALSE:JNI_TRUE;
}
APP的调用方式
System.loadLibrary("ext_fm_jni");
注意打包函数so时,需要注意下Android.mk的module名字的形式一定得加上lib的前缀libext_fm_jni,最终生成肯定也是libext_fm_jni.so供java调用
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_SRC_FILES := \
common.cpp \
ext_fm.cpp \
fmr_core.cpp
LOCAL_C_INCLUDES := $(JNI_H_INCLUDE) \
frameworks/base/include/media
LOCAL_SHARED_LIBRARIES := \
libcutils \
libdl \
libmedia \
liblog
LOCAL_MODULE := libext_fm_jni
LOCAL_CFLAGS += \
-Wno-error=unused-parameter \
-Wno-error=deprecated-register
新增加ExtFm.java,直接参考源码的实现就可以,唯一的区别,修改为public方法以供调用
package com.android.fmradio;
/**
* This class define FM native interface, will description FM native interface
*/
public class FmNative {
static {
System.loadLibrary("fmjni");
}
...
/**
* power up FM with frequency use long antenna
*
* @param frequency frequency(50KHZ, 87.55; 100KHZ, 87.5)
*
* @return (true, success; false, failed)
*/
public static native boolean powerUp(float frequency);
...