应用层

Demo路径:

packages/apps/Car/Radio

BootupReceiver类监听了ACTION_BOOT_COMPLETED,然后在onReceive里启动了RadioService.

CarRadioActivity作为主Activity,在onCreate里创建了mRadioController.

收音机主界面有个RadioDrawer(收音机抽屉).点击其中一个就会调用

mRadioController.openRadioBand(SUPPORTED_RADIO_BANDS[position]);

SUPPORTED_RADIO_BANDS定义

private static final int[] SUPPORTED_RADIO_BANDS = new int[] {RadioManager.BAND_AM, RadioManager.BAND_FM };

虽然没看到实际界面,其实这个地方也就是选择AM或者FM,大家都玩过收音机,完全可以自己脑补!

openRadioBand@RadioController:

...
mRadioManager.openRadioBand(radioBand);
...

fuck这个mRadioManager并不是RadioManager类型的.而是com.android.car.radio.service.IRadioManager

总之实现是在这里:

./src/com/android/car/radio/RadioService.java

//openRadioBand@RadioService
...openRadioBandInternal(radioBand);
...

openRadioBandInternal@RadioService

if (mRadioTuner != null) {mRadioTuner.setConfiguration(config);
} else {mRadioTuner = mRadioManager.openTuner(mModules.get(0).getId(), config, true,mInternalRadioTunerCallback, null /* handler */);
}

这个mRadioManager才是真的android.hardware.radio.RadioManager的(Android的开发者命名的时候不怕我们混淆吗?).

framework层

先把用得到的几个类堆出来一下:

//The RadioManager class allows to control a broadcast radio tuner present on the //device.It provides data structures and methods to query for available radio modules,
//list their properties and open an interface to control tuning operations and receive callbacks when asynchronous operations complete or events occur.
base/core/java/android/hardware/radio/RadioManager.java
//Implements the RadioTuner interface by forwarding calls to radio service.
base/core/java/android/hardware/radio/TunerAdapter.java
/*** RadioTuner interface provides methods to control a radio tuner on the device: selecting and* configuring the active band, muting/unmuting, scanning and tuning, etc...** Obtain a RadioTuner interface by calling {@link RadioManager#openTuner(int,* RadioManager.BandConfig, boolean, RadioTuner.Callback, Handler)}.* @hide*/
base/core/java/android/hardware/radio/RadioTuner.java
//package底下,car相关
//A representation of a radio station.
//电台相关,比如说频道名字,频段之类的
apps/Car/libs/car-radio-service/src/com/android/car/radio/service/RadioStation.java
//A set of identifiers necessary to tune to a given station.
/* This can hold various identifiers, like* - AM/FM frequency* - HD Radio subchannel* - DAB channel info*/
base/core/java/android/hardware/radio/ProgramSelector.java

其中,最上层的RadioManager这个类.我们可以简单的提炼一下

public class RadioManager {//列出给定广播无线电模块支持的属性,选项和无线电频段。//调用RadioManager APIs的时候,每一个module都有唯一的ID用来定位自己.//Module properties are returned by {@link #listModules(List <ModuleProperties>)} method.public static class ModuleProperties implements Parcelable/** Radio band descriptor: an element in ModuleProperties bands array.* It is either an instance of {@link FmBandDescriptor} or {@link AmBandDescriptor} */public static class BandDescriptor implements Parcelablepublic static class FmBandDescriptor extends BandDescriptorpublic static class AmBandDescriptor extends BandDescriptor...public static class BandConfig implements Parcelablepublic static class FmBandConfig extends BandConfigpublic static class AmBandConfig extends BandConfig//广播节目信息(Program也有节目的意思)/** Radio program information returned by* {@link RadioTuner#getProgramInformation(RadioManager.ProgramInfo[])} */public static class ProgramInfo implements Parcelable...
}

接下来直接捋一捋整个过程吧!

mRadioManager.openTuner

//Open an interface to control a tuner on a given broadcast radio module.
//Optionally selects and applies the configuration passed as "config" argument.
public RadioTuner openTuner(int moduleId, BandConfig config, boolean withAudio,RadioTuner.Callback callback, Handler handler) {...ITuner tuner;TunerCallbackAdapter halCallback = new TunerCallbackAdapter(callback, handler);try {tuner = mService.openTuner(moduleId, config, withAudio, halCallback);} catch (RemoteException e) {Log.e(TAG, "Failed to open tuner", e);return null;}...return new TunerAdapter(tuner, config != null ? config.getType() : BAND_INVALID);}

参数解释:

moduleId:radio module identifier {@link ModuleProperties#getId()}. Mandatory(强制的).

config:desired band and configuration to apply when enabling the hardware module.optional, can be null.

withAudio:

​ {@code true} to request a tuner with an audio source.This tuner is intended for (打算用于)live listening or recording or a radio program(直播或者录节目).

​ If {@code false}, the tuner can only be used to retrieve program informations(检索节目信息).

(可以简单理解成没有音频数据吗?true的时候是需要音频数据的,false的时候只是要扫描频道?)

剩下的,比较常见,就不多说了.

可以看到,具体的实现是两步

(1)binder调用openTuner

tuner = mService.openTuner(moduleId, config, withAudio, halCallback);

(2)用Service进程返回的ITuner对象构造TunerAdapter对象并返回

return new TunerAdapter(tuner, config != null ? config.getType() : BAND_INVALID);

那我们就先看看mService.再看看TunerAdapter.

mService是一个IRadioService,对应的实现类是:

frameworks/base/services/core/java/com/android/server/broadcastradio/BroadcastRadioService.java里的子类ServiceImpl

private class ServiceImpl extends IRadioService.Stub {private void enforcePolicyAccess() {if (PackageManager.PERMISSION_GRANTED != getContext().checkCallingPermission(Manifest.permission.ACCESS_BROADCAST_RADIO)) {throw new SecurityException("ACCESS_BROADCAST_RADIO permission not granted");}}
​@Overridepublic List<RadioManager.ModuleProperties> listModules() {enforcePolicyAccess();synchronized (mLock) {...mModules = nativeLoadModules(mNativeContext);...return mModules;}}
​@Overridepublic ITuner openTuner(int moduleId, RadioManager.BandConfig bandConfig,boolean withAudio, ITunerCallback callback) {enforcePolicyAccess();...synchronized (mLock) {return nativeOpenTuner(mNativeContext, moduleId, bandConfig, withAudio, callback);}}}

仅仅提供了两个访问接口listModules和openTuner,都是native方法.调用之前都要检查下

Manifest.permission.ACCESS_BROADCAST_RADIO

这条权限.

继续看这个native方法

路径:base/services/core/jni/BroadcastRadio/BroadcastRadioService.cpp

static jobject nativeOpenTuner(JNIEnv *env, jobject obj, long nativeContext, jint moduleId,jobject bandConfig, bool withAudio, jobject callback) {...auto& ctx = getNativeContext(nativeContext);... //AM,FM的id不一样?auto module = ctx.mModules[moduleId];...auto tuner = make_javaref(env, env->NewObject(gjni.Tuner.clazz, gjni.Tuner.cstor,callback, halRev, region, withAudio, bandConfigHal.type));...auto tunerCb = Tuner::getNativeCallback(env, tuner);//调用hidl层的openTuner方法auto hidlResult = module->openTuner(bandConfigHal, withAudio, tunerCb,[&](Result result, const sp<ITuner>& tuner) {halResult = result;//这个写法没见过哦,虽然大概能猜的出来halTuner = tuner;});Tuner::assignHalInterfaces(env, tuner, module, halTuner);return tuner.release();
}

继续看hidl层的openTuner方法

收音机hidl层的位置

hardware/interfaces/broadcastradio/1.0

//default/BroadcastRadio.cpp
Return<void> BroadcastRadio::openTuner(const BandConfig& config, bool audio,const sp<ITunerCallback>& callback, openTuner_cb _hidl_cb)
{sp<Tuner> tunerImpl = new Tuner(callback, this);
​radio_hal_band_config_t halConfig;const struct radio_tuner *halTuner;Utils::convertBandConfigToHal(&halConfig, &config);int rc = mHwDevice->open_tuner(mHwDevice, &halConfig, audio,Tuner::callback, tunerImpl.get(),&halTuner);if (rc == 0) {tunerImpl->setHalTuner(halTuner);}
​_hidl_cb(Utils::convertHalResult(rc), tunerImpl);return Void();
}

mHwDevice是radio_hw_device类型,再往下找和Android O之前就比较类似了

radio的hal层路径:

hardware/libhardware/modules/radio

mHwDevice调用的openTuner就是在这里啦

//radio_hw.c
static int rdev_open_tuner(const struct radio_hw_device *dev,const radio_hal_band_config_t *config,bool audio,radio_callback_t callback,void *cookie,const struct radio_tuner **tuner)
{//转换为本地结构体struct stub_radio_device *rdev = (struct stub_radio_device *)dev;...rdev->tuner = (struct stub_radio_tuner *)calloc(1, sizeof(struct stub_radio_tuner));...//映射一堆本地方法rdev->tuner->interface.set_configuration = tuner_set_configuration;rdev->tuner->interface.get_configuration = tuner_get_configuration;rdev->tuner->interface.scan = tuner_scan;rdev->tuner->interface.step = tuner_step;rdev->tuner->interface.tune = tuner_tune;rdev->tuner->interface.cancel = tuner_cancel;rdev->tuner->interface.get_program_information = tuner_get_program_information;
​rdev->tuner->audio = audio;rdev->tuner->callback = callback;rdev->tuner->cookie = cookie;
​rdev->tuner->dev = rdev;...
}

再往下我们先不仔细研究.我们先回过头去接着看TunerAdapter这个类

//Implements the RadioTuner interface by forwarding calls to radio service.
//通过转移调用到radioService实现了RadioTuner interface.
class TunerAdapter extends RadioTuner {...//里面封装的所有函数都是用这个mTuner调用的.private final ITuner mTuner;...//这里以这个常见的调频函数举例@Overridepublic void tune(@NonNull ProgramSelector selector) {try {mTuner.tune(selector);} catch (RemoteException e) {throw new RuntimeException("service died", e);}}//收音机还有图片?什么图片?频道logo???@Overridepublic @Nullable Bitmap getMetadataImage(int id) {try {return mTuner.getImage(id);} catch (RemoteException e) {throw new RuntimeException("service died", e);}}
}

ITuner实现在这里

frameworks/base/services/core/java/com/android/server/broadcastradio/Tuner.java

class Tuner extends ITuner.Stub {...//接着用调频函数举例@Overridepublic void tune(ProgramSelector selector) {if (selector == null) {throw new IllegalArgumentException("The argument must not be a null pointer");}Slog.i(TAG, "Tuning to " + selector);synchronized (mLock) {checkNotClosedLocked();nativeTune(mNativeContext, selector);}}...
}

ProgramSelector是一个新出现,且看着有点重要的类,稍后再说.

这里Tuner的实现通过调用nativeTune方法来实现

很好找,和BroadcastRadioService.cpp(openTuner的native)在同一个文件夹

frameworks/base/services/core/jni/BroadcastRadio/Tuner.cpp

static void nativeTune(JNIEnv *env, jobject obj, jlong nativeContext, jobject jSelector) {...auto& ctx = getNativeContext(nativeContext);auto halTuner10 = getHalTuner(ctx);auto halTuner11 = ctx.mHalTuner11;...auto selector = convert::ProgramSelectorToHal(env, jSelector);if (halTuner11 != nullptr) {convert::ThrowIfFailed(env, halTuner11->tuneByProgramSelector(selector));} else {uint32_t channel, subChannel;if (!V1_1::utils::getLegacyChannel(selector, &channel, &subChannel)) {jniThrowException(env, "java/lang/IllegalArgumentException","Can't tune to non-AM/FM channel with HAL<1.1");return;}convert::ThrowIfFailed(env, halTuner10->tune(channel, subChannel));}
}

简单来看,就是准备参数,调用halTuner11->tuneByProgramSelector(selector)或者halTuner10->tune(channel, subChannel).总之,和BroadcastRadioService.cpp同样进入了HIDL层的调用了.

这个时候,综合一下hidl层中IBroadcastRadio.hal文件和ITuner.hal文件

hardware/interfaces/broadcastradio/1.0/IBroadcastRadio.hal

interface IBroadcastRadio {getProperties() generates (Result result, Properties properties);openTuner(BandConfig config, bool audio, ITunerCallback callback)generates (Result result, ITuner tuner);
}

hardware/interfaces/broadcastradio/1.0/ITuner.hal

interface ITuner {...setConfiguration(BandConfig config) generates(Result result);getConfiguration() generates(Result result, BandConfig config);scan(Direction direction, bool skipSubChannel) generates(Result result);step(Direction direction, bool skipSubChannel) generates(Result result);tune(uint32_t channel, uint32_t subChannel) generates(Result result);...
}

简单总结下

存在两条脉络,broadcastradio(文件夹名,类名等等都是)这个无线电广播类是用来打开调频器的(openTuner).而Tuner这条脉络是调频的具体功能的.设置配置,获取配置,扫描,调台等等.

接下来研究下,刚才跳过的ProgramSelector这个东西.前面说过了,词典告诉我们Program还有节目的意思,那这个类大约就是"节目选择器的意思了".

public final class ProgramSelector implements Parcelable {//各种各样的看不懂.
}

先看看这个方法:

frameworks/base/services/core/jni/BroadcastRadio/Tuner.cpp

static void nativeTune(JNIEnv *env, jobject obj, jlong nativeContext, jobject jSelector) {...auto selector = convert::ProgramSelectorToHal(env, jSelector);if (halTuner11 != nullptr) {convert::ThrowIfFailed(env, halTuner11->tuneByProgramSelector(selector));} else {...convert::ThrowIfFailed(env, halTuner10->tune(channel, subChannel));}
}

hidl层分两个版本接口,1.0,1.1.这个地方调用方式都有点不一样.但是

hardware/interfaces/broadcastradio/1.1/default/Tuner.cpp:

Return<Result> Tuner::tune(uint32_t channel, uint32_t subChannel) {ALOGV("%s(%d, %d)", __func__, channel, subChannel);Band band;{lock_guard<mutex> lk(mMut);band = mAmfmConfig.type;}return tuneByProgramSelector(utils::make_selector(band, channel, subChannel));
}
​
Return<Result> Tuner::tuneByProgramSelector(const ProgramSelector& sel) {...
}

hardware/interfaces/broadcastradio/1.0/default/Tuner.cpp

Return<Result> Tuner::tune(uint32_t channel, uint32_t subChannel)  {if (mHalTuner == NULL) {return Utils::convertHalResult(-ENODEV);}int rc = mHalTuner->tune(mHalTuner, channel, subChannel);return Utils::convertHalResult(rc);
}

都有tune方法,参数类型也一样,V1.1的直接可以自己指定Selector?

再回头看

/frameworks/base/core/java/android/hardware/radio/ProgramSelector.java

中的方法createAmFmSelector

public static @NonNull ProgramSelector createAmFmSelector(@RadioManager.Band int band, int frequencyKhz, int subChannel) {...
}

需要指定frequencyKhz,指定hz.

那我猜是这样的

tune接口呢,一开始只能说,我想调到第几台这种.后来1.1的接口呢,可以直接命令调到99.9kHZ这种.

那ProgramSelector是一个节目选择器,调台器?Aha!

Android O 收音机学习研究(基于Car)相关推荐

  1. android 人脸识别 方法研究,基于Android平台的人脸识别技术研究

    摘要: 在计算机视觉与模式识别领域中,人脸检测与识别技术是一个非常热门的研究课题,同时也具备非常广阔的商业价值.在诸多的目标检测算法中,基于AdaBoost算法的目标检测方法具有检测速度快,检测效果好 ...

  2. 基于android的交流平台,基于Android的移动学习交流平台的设计与实现

    摘要: 随着移动互联网技术的不断发展,智能手机的不断普及,现在越来越多的人通过手机等智能设备来进行学习和交流.为了满足教师和学生实时的沟通交流,提高学生的学习兴趣和效率,本文设计了基于Android的 ...

  3. 基于Android的学生学习打卡监督系统

    文档+任务书+选题申请表+开题报告+开题答辩PPT+项目源码 毕 业 论 文 目 录 1.绪论 1 1.1课题开发背景 2 1.2课题开发目的及意义 3 2.系统分析 3 2.1研究目标 3 2.2需 ...

  4. 基于android单词本分析与实现,基于Android的单词学习系统设计与实现

    摘要: 随着中国国际化程度的提高,英语的普遍性和重要性日益凸显.英语作为一门语言,其基础是词汇,英文词汇量的扩充是提高英文水平的基础.学习者对英文词汇的学习包括遇到生词时的单词查询和有计划的词汇记忆. ...

  5. 基于Android实现日语学习app设计与实现演示【附项目源码+论文说明】分享

    基于Android实现日语学习app设计与实现演示 摘要 随着手机使用的普及,人们获取与保存信息的方式已经发生了激动人心的转变.智能手机正在逐步融入我们的生活,并影响和改变着我们的生活.由于现在各种智 ...

  6. 基于Android实现日语学习app设计与实现演示【附项目源码+论文说明】

    基于Android实现日语学习app设计与实现演示 摘要 随着手机使用的普及,人们获取与保存信息的方式已经发生了激动人心的转变.智能手机正在逐步融入我们的生活,并影响和改变着我们的生活.由于现在各种智 ...

  7. 基于Android的英语学习APP的设计与实现

    随着移动互联网技术的快速发展,利用手机APP进行工作学习已经成为大众非常喜欢的生活方式.作为传统英语课外学习的重要补充,为了更好的提升英语学习的效果,设计一款适合学生学习的英语学习APP成为关注的焦点 ...

  8. 基于 Android NDK 的学习之旅-----资源释放

    基于 Android NDK 的学习之旅-----资源释放 做上一个项目的时候因为与C引擎交互频繁,有时候会突然莫名其妙的的整个应用程序直接挂掉.因为我是学Java 开始的,所以对主动释放内存没多大概 ...

  9. 基于 Android NDK 的学习之旅----- C调用Java

    2019独角兽企业重金招聘Python工程师标准>>> 基于 Android NDK 的学习之旅----- C调用Java 许多成熟的C引擎要移植到Android 平台上使用 , 一 ...

最新文章

  1. 机器学习:计算学习理论
  2. 57张图,13个实验,干死 MySQL 锁!
  3. 简单的form表单操作的几种写法
  4. Linux出现cannot create temp file for here-document: No space left on device的问题解决
  5. 交换机的基本功能和应用就是集中连接网络设备
  6. kubernetes实践之运行aspnetcore webapi微服务
  7. [转]浅谈CMD和win powershell的区别
  8. 伪共享和缓存行填充,Java并发编程还能这么优化!
  9. Snipaste的安装及快捷键的设置
  10. 在阿里云服务器centOs7系统中部署.NET Core项目
  11. 设计模式 AOP 面向切入编程
  12. Navicat Premium相关注册码
  13. i5 9600k和i5 9400的差距?哪个好?对比才知道
  14. RNA-seq数据分析
  15. CF 581A Vasya the Hipster
  16. Flutter Package 开发、发布、使用
  17. 让电脑说话vbs程序
  18. cout 声明与定义
  19. 鸽主姓名查询成绩_获奖鸽主姓名,名次及足环号码
  20. 整理了60个Python小例子,拿来即用!

热门文章

  1. GridControl GridView 单元格内容换行
  2. 用FreeBSD10搭建基于ZFS的iSCSI服务
  3. 基层管理者的角色定位
  4. python暴力破解excel_使用 Python 读写 Excel 文件(一)
  5. 台式电脑主机与屏幕的连接线
  6. [kubernetes]-通过initcontainer监控job是否完成
  7. 俺老孙画个圈-板框与安装孔-PCB系列教程1-10
  8. 给微信新增的群接龙功能加一个AI, 增长黑客必备技能
  9. 云测试平台(testin)
  10. h5中performance.timing轻松获取网页各个数据 如dom加载时间 渲染时长 加载完触发时间...