【Android】Radio HAL JNI JAVA 三层结构解析

最近有个项目需要在Android Radio中增加一个远程/近程功能,犹豫Android原生代码没有提供该功能,因此只能自己修改Android源码来完成该功能。(说明:修改远程/近程功能实质就是修改Radio搜索时候传递的参数值,暂且理解成设置’门限‘)由于要修改Android底层源码,因此得先了解Android框架结构。
Android系统结构
其实HAL层在这个位置,HAL层主要是为保护硬件厂商知识产权等商业利益而产生的。
其存在主要有几个原因:
1.硬件厂商的linux kernel接口并不都是标准统一的
2.kernel driver是GPL开源,而硬件厂商为了保护自身利益不公开硬件驱动,采用HAL方式绕过GPL。
3.个别硬件的特殊需求
简化下来:
查看源码需要先了解Android Binder通信机制,这里推荐《深入理解Android 卷1》有单独章节讲解相关知识(这里就不详细介绍)。
这里我说点题外话,我是在Ubuntu上开发,装的Wine+Source insight查看源码,很方便。
我们从底层开始逐层添加我们的函数来完成功能
1.HAL层:
Android源代码结构:
Google提供的Android包含了原始Android的目标机代码,主机编译工具、仿真环境,代码包经过解压缩后,第一级别的目录和文件如下所示:
.
|-- Makefile        (全局的Makefile)
|-- bionic          (Bionic含义为仿生,这里面是一些基础的库的源代码)
|-- bootloader      (引导加载器)
|-- build           (build目录中的内容不是目标所用的代码,而是编译和配置所需要的脚本和工具)
|-- dalvik          (JAVA虚拟机)
|-- development     (程序开发所需要的模板和工具)
|-- external        (目标机器使用的一些库)
|-- frameworks      (应用程序的框架层)
|-- hardware        (与硬件相关的库)
|-- kernel          (Linux2.6的源代码)
|-- packages        (Android的各种应用程序)
|-- prebuilt        (Android在各种平台下编译的预置脚本)
|-- recovery        (与目标的恢复功能相关)
`-- system          (Android的底层的一些库)
其中 hardware\Libhardware\Modules\Radio\Radio_hw.c 文件就是HAL与驱动交互层代码
1.1 定义函数
static int tuner_switch_loc_or_rem(const struct radio_tuner *tuner) {...}
1.2 rdev_open_tuner 中注册函数
rdev->tuner->interface.switch_loc_or_rem = tuner_switch_loc_or_rem;
注意:
一般来说HAL moudle需要涉及的是三个关键结构体:
struct hw_module_t;   
struct hw_module_methods_t;
struct hw_device_t;
这三个结构体定义在hardware.h中,其作用就是定义设备,查找设备,操作设备。 Radio_hw.c 就是做了相关操作
我们的radio模块就是通过ID来找到对应的硬件模块
struct radio_module HAL_MODULE_INFO_SYM = {
.common = {
.tag = HARDWARE_MODULE_TAG,
.module_api_version = RADIO_MODULE_API_VERSION_1_0,
.hal_api_version = HARDWARE_HAL_API_VERSION,
.id = RADIO_HARDWARE_MODULE_ID,
.name = "Stub radio HAL",
.author = "The Android Open Source Project",
.methods = &hal_module_methods,
},
};

1.3 JNI native调用(方法回调)
设备定义好了那么就该由上层JNI native代码调用了,注意1.2中的tuner : stub_radio_tuner 类型
hardware\Libhardware\include\Radio.h (注意不是framework\av\Include\radio\radio.h)这个类就封装了1.2中所说的那几个结构体和Radio相关的属性和功能
因此我们直接在这里添加我们的回调函数
1.3.1 添加回调函数
int (*switch_loc_or_rem)(const struct radio_tuner *tuner);
2. JNI层:
2.1 由于JNI层和Native(HAL)层是通过Binder通信的 ,大致如下图
2.2 Binder 服务端
这里说到了 Binder,大家一定要去看下Binder机制,分清Bp Bn,sp wp
首先我们来到native层紧密相关的framwork\av\services\radio\RadioService.h 和 framwork\av\services\radio\RadioService.cpp
我们需要在ModuleClient模块(继承 BnRadio 说明是本地Native)添加相应的接口:
定义:
virtual status_t switchLocOrRem(bool *bSwitched);
实现:
status_t RadioService::ModuleClient::switchLocOrRem(bool *bSwitched){...}
RadioService其实就是Binder服务端,关联的是BnRadio,处理Raido事务。那么当然该转到Binder的客户端了
2.3 Binder 客户端
framework\av\include\radio\radio.h
framework\av\radio\radio.cpp
framework\av\include\radio\IRadio.h
framework\av\radio\IRadio.cpp
跟踪代码不难发现有这么一条线路Radio->IRadio->remote()->transact();而IRadio就是和 BpRadio绑定的。
2.4.JNI
JNI其实就是一套规范而已
RadioModule 是提供给上层JAVA使用的一套native接口,其路径为framework\base\core\java\android\hardware\radio\RadioModule.java
当然我们也要定义我们的native接口:
public native boolean switchLocOrRem(); // 定义好后就可以调用通过JNI调用了
RadioTuner 提供JAVA接口
framework\base\core\java\android\hardware\radio\RadioTuner.java
public abstract boolean switchLocOrRem();
这里有个地方需要注意:
public RadioTuner openTuner(int moduleId, BandConfig config, boolean withAudio,
RadioTuner.Callback callback, Handler handler) {
if (callback == null) {
return null;
}
RadioModule module = new RadioModule(moduleId, config, withAudio, callback, handler);
if (module != null) {
if (!module.initCheck()) {
module = null;
}
}
return (RadioTuner)module;
}
RadioModule 转换成了JAVA使用的 RadioTuner
这里就打通了JAVA->JNI道路了。
2.4.1定义和实现我们的JNI函数
路径:framework\base\core\jni\android_hardware_Radio.cpp中定义:
static jboolean
android_hardware_Radio_switchLocOrRem(JNIEnv *env, jobject thiz){
ALOGV("%s", __FUNCTION__);
sp< Radio> module = getRadio(env, thiz);
if (module == NULL) {
return false;
}

bool bSwitched;
status_t status = module->switchLocOrRem(&bSwitched);
if (status != NO_ERROR) {
return false;
}

return (jboolean)bSwitched;
}
这里面就是JNI调用本地native代码,通过Radio的binder机制实现的。
2.4.2 添加注册数组中
static JNINativeMethod gModuleMethods[] ={
...
{"switchLocOrRem",
"()Z",
(void *)android_hardware_Radio_switchLocOrRem}, //关联Radio中的函数
}
这样JAVA层就可以通过RadioModule 来操作Radio了
3.JAVA层
3.1首先导入Radio相关的aidl文件
Radio相关的处理类
3.2 定义serviceconnection
private ServiceConnection mServiceConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName className, IBinder binder) {
Log.d(TAG, "onServiceConnected()");
mRadioManager = (( IRadioManager) binder) ; // 返回的是3.4中的mBinder
try {
if (mRadioManager == null || !mRadioManager.isInitialized()) {
// mRadioDisplayController.setEnabled(false);
return;
}
mRadioManager.addRadioTunerCallback(mCallback);
int radioBand = mRadioStorage.getRadioBand();
openRadioBand(radioBand);
unMute();
if (mCurrentChannelNumber != -1){
RadioStation radioStation = new RadioStation(mCurrentChannelNumber, 0, mCurrentRadioBand, null);
if (radioStation != null){
Log.d(TAG, "onServiceConnected --first radition...");
tuneToRadioChannel(radioStation);
}
}
} catch (RemoteException e) {
Log.e(TAG, "onServiceConnected(); remote exception: " + e.getMessage());
}
}
3.2 bind service
Intent bindIntent = new Intent(mActivity, RadioService.class);
if (!mActivity.bindService(bindIntent, mServiceConnection, Context.BIND_AUTO_CREATE)) {
Log.e(TAG, "Failed to connect to RadioService.");
}
3.3 IRadioManager.Stub 完成重載
private IRadioManager.Stub mBinder = new IRadioManager.Stub(){
...
@Override
public boolean switchLocOrRem(){
if (mRadioTuner == null) {
return false;
}
return mRadioTuner.switchLocOrRem();
}
}
3.4 事件响应 一切源于此处触发......
IRadioManager mRadioManager;
mRadioManager.switchLocOrRem();
好了我们现在从上到下来此梳理:


  • 2
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值