当前位置: 首页 > news >正文

Android Radio2.0——设置广播配置标志(一)

        在 Android Radio 中,RDS (Radio Data System) 是一种在调频(FM)广播信号中嵌入数字信息的技术,它可以携带额外的数据信息,如电台名称、节目信息等。

一、广播配置设置

        在介绍 RDS 广播配置设置前我们先来了解一些常见的 RDS 代码及其含义:

  • AF (Alternative Frequencies):表示备用频率。当主频率受到干扰或者信号质量下降时,接收器可以自动切换到这些备用频率上继续收听同一个广播节目。
  • REG (Regional Information):区域信息。用于发送与特定地区相关的文本信息,例如交通状况或天气预报等。
  • TP (Traffic Programme):交通节目标志。如果一个电台主要播放交通信息,它会发送这个标志。接收器可以根据这个标志来决定是否锁定该电台。
  • TA (Traffic Announcement):交通公告。当有重要的交通信息(如道路封闭、事故等)时,电台会发送这个标志。接收器可以被设置为自动切换到发送 TA 标志的电台。

        这些信息都是通过 RDS 系统传输的附加数据,使得 FM 收音机可以提供更多的功能和服务,例如自动频率切换、交通信息提示等。这些功能增强了用户体验,并提供了更加丰富的广播内容交互方式。

1、RDS配置

private final AtomicBoolean isTAOpen = new AtomicBoolean(false);
private final AtomicBoolean isTPOpen = new AtomicBoolean(false);private static final String DEFAULT_SETTINGS = "[0,0,1,0]";
private static final int STATUS_OPEN = 1;private final RadioTuner mRadioTuner;// 接口调用
setRadioRDSSettings(DEFAULT_SETTINGS)/*** 配置Radio相关功能*/
private void setRadioRDSSettings(String value) {int[] settings = StringUtils.toIntArray(value);//{AF,REG,TP,TA}boolean isAFOpen = settings[0] == STATUS_OPEN;boolean isREGOpen = settings[1] == STATUS_OPEN;isTAOpen.set(settings[2] == STATUS_OPEN);isTPOpen.set(settings[3] == STATUS_OPEN);try {// 设置RDS配置mRadioTuner.setConfigFlag(RadioManager.CONFIG_RDS_AF, isAFOpen);mRadioTuner.setConfigFlag(RadioManager.CONFIG_RDS_REG, isREGOpen);} catch (Exception e) {e.printStackTrace();}
}

        这里主要是通过 setConfigFlag 函数设置 AF 和 REG 的开关状态。对于 TP 和 TA 的开关状态设置我们放到后面讲。

2、AtomicBoolean

        可以发现上面函数中 TP 和 TA 开关状态使用了 AtomicBoolean 数据类型,它与 Boolean 类型的数据有什么区别是什么?这里我们通过与基本类型 Boolean 进行对比来了解 AtomicBoolean类型。

Boolean

  • 数据类型:Java 中 boolean 基本类型的包装类。Boolean 对象可以存储 true 或 false 值,并且提供了与基本类型 boolean 相关的一些静态方法。
  • 线程安全性:不具备任何内置的线程安全机制。如果你需要在线程间共享一个 Boolean 对象,并且多个线程可能会修改它的值,你需要自己负责同步(例如使用 synchronized 关键字或其他并发工具)。
  • 方法和功能:提供了基本的操作,如 toString()、equals()、hashCode() 等,以及一些静态工厂方法如 valueOf()、parseBoolean() 等。
  • 性能:需要在多线程环境中保证线程安全,通常需要使用同步机制,这可能会引入性能开销。
  • 使用场景:适合于单线程环境或已经妥善处理了同步问题的多线程环境。

AtomicBoolean 

  • 数据类型:java.util.concurrent.atomic 包中的一个类,它是 Boolean 类型的一个变种,专为高并发环境设计。
  • 线程安全性:提供了原子操作的支持,这意味着它可以在线程安全的方式下更新其值。AtomicBoolean 内部使用了 CAS(Compare and Swap)算法来保证更新操作的原子性,因此不需要显式的同步机制就可以安全地在线程间共享和修改。
  • 方法和功能:提供了一些专门用于原子操作的方法,如 get()、compareAndSet()、getAndSet() 等,这些方法允许你在不担心线程竞争的情况下读取和修改 AtomicBoolean 的值。
  • 性能:由于 AtomicBoolean 内置了原子操作的支持,因此在高并发环境下通常比手动同步的 Boolean 对象更高效。
  • 使用场景:适合于高并发环境,特别是当多个线程需要并发访问和修改一个布尔值时。

        综上所述 AtomicBoolean 是专门为解决多线程环境下的布尔值更新问题而设计的,它提供了线程安全的原子操作,使得在并发编程中更加方便和高效。而 Boolean 则是一个普通的包装类,适用于不需要考虑线程安全的场景。

3、配置类型

源码位置:/frameworks/base/core/java/android/hardware/radio/RadioManager.java

  • CONFIG_FORCE_MONO:强制接收单声道音频流。在模拟广播(如AM/FM)中,当接收条件较差时,可以通过将立体声通道合并为单声道来改善接收效果。
  • CONFIG_FORCE_ANALOG:强制使用模拟播放。用户可以选择禁用数字播放(如 FM HD Radio 或混合 FM/DAB)。这是一种用户选择,不反映 HAL 实现中的数字-模拟切换状态。
  • CONFIG_FORCE_DIGITAL:强制使用数字播放。用户可以选择禁用在接收条件较差时发生的数字-模拟切换。在这种模式下,如果数字信号不可用,广播将保持静音,而不是切换到模拟频道。
  • CONFIG_RDS_AF:启用 RDS 备选频率(Alternative Frequencies)。如果当前调谐的 RDS 电台在多个频道上广播,接收器将自动切换到最佳可用的备选频道。
  • CONFIG_RDS_REG:启用 RDS 区域锁定(region-specific program lock-down)。允许用户在进入其他区域时锁定当前区域的广播内容。
  • CONFIG_DAB_DAB_LINKING:启用 DAB-DAB 硬链接和隐式链接(相同内容)。在 DAB 广播中,链接两个具有相同内容的频道。
  • CONFIG_DAB_FM_LINKING:启用 DAB-FM 硬链接和隐式链接(相同内容)。 在 DAB 和 FM 广播之间链接具有相同内容的频道。
  • CONFIG_DAB_DAB_SOFT_LINKING:启用 DAB-DAB 软链接(相关内容)。在 DAB 广播中,链接两个具有相关内容的频道。
  • CONFIG_DAB_FM_SOFT_LINKING:启用 DAB-FM 软链接(相关内容)。在 DAB 和 FM 广播之间链接具有相关内容的频道。

        这里的 DAB 广播是一种完全数字化的广播技术,旨在替代传统的模拟广播(如 AM/FM),它不仅提供了更高品质的音频和更丰富的多媒体服务,同时还包括文本信息、静止图片、甚至低带宽视频。甚至可以提供接近 CD 质量的声音,并且具有更强的抗干扰能力和更好的移动接收性能。 

二、接口调用

1、RadioTuner

源码位置:/frameworks/base/core/java/android/hardware/radio/RadioTuner.java

public void setConfigFlag(@RadioManager.ConfigFlag int flag, boolean value) {throw new UnsupportedOperationException();
}

        这里如果传入的标志类型不适用会抛出 IllegalStateException。对于 RadioTuner 类提供了 setConfigFlag() 对应的接口,这里与前面的流程基本相同,TunerAdapter 实现了该接口。

2、TunerAdapter

源码位置:/frameworks/base/core/java/android/hardware/radio/TunerAdapter.java

class TunerAdapter extends RadioTuner {@NonNull private final ITuner mTuner;……@Overridepublic void setConfigFlag(@RadioManager.ConfigFlag int flag, boolean value) {try {mTuner.setConfigFlag(flag, value);} catch (RemoteException e) {throw new RuntimeException("service died", e);}}
}

        这里同样通过 ITuner AIDL 接口调用 /hal1/Tuner.java 或 /hal2/TunerSession.java,后面的文章我们将不会再去关注 hal1 部分的代码。

3、TunerSession

源码位置:/frameworks/base/services/core/java/com/android/server/broadcastradio/hal2/TunerSession.java

class TunerSession extends ITuner.Stub {private final ITunerSession mHwSession;@Overridepublic void setConfigFlag(int flag, boolean value) throws RemoteException {Slog.v(TAG, "setConfigFlag " + ConfigFlag.toString(flag) + " = " + value);synchronized (mLock) {checkNotClosedLocked();// 调用 HAL 层接口int halResult = mHwSession.setConfigFlag(flag, value);Convert.throwOnError("setConfigFlag", halResult);}}
}

        这里主要通过 mHwSession 调用硬件抽象层(HAL)的 setConfigFlag 方法。

4、TunerSession(HAL)

TunerSession.h

源码位置:/hardware/interfaces/broadcastradio/2.0/default/TunerSession.h

struct TunerSession : public ITunerSession {virtual Return<Result> setConfigFlag(ConfigFlag flag, bool value);
}

        可以看到 Hal 层的 TunerSession 继承 ITunerSession,并实现对应的  setConfigFlag() 函数。

TunerSession.cpp

源码位置:/hardware/interfaces/broadcastradio/2.0/default/TunerSession.cpp

Return<Result> TunerSession::setConfigFlag(ConfigFlag flag, bool value) {LOG(VERBOSE) << __func__ << " " << toString(flag) << " " << value;return Result::NOT_SUPPORTED;
}

        可以看到这段代码只是一个占位符实现,表明当前 TunerSession 类不支持设置配置标志的功能。如果需要实现具体的配置逻辑,可以在 setConfigFlag 方法中添加相应的代码。如果确实不支持该功能,则可以保留当前返回 Result::NOT_SUPPORTED 的实现。

        因此,如果需要实现对应功能,需要自行实现 Hal 层 TunerSession 中的对应函数,对于 Hal 层并不是我们主要关注的只是点,这里我们简单介绍一下思路即可:

  • 上面 setConfigFlag() 函数中调用 TunerHwAdapter 中的对应函数,其中 TunerHwAdapter 自定义接口类。
  • TunerHwAdapter 中的 setConfigFlag() 函数调用 RdsHwService/DabHwService 中的对应函数,RdsHwService/DabHwService 是我们创建的与对应硬件交互的类。

http://www.mrgr.cn/news/19290.html

相关文章:

  • 一般位置下的3D齐次旋转矩阵
  • Google Earth Engine(python)—— sentinel-2 卫星图像根据经纬度画图
  • Flowable获取下一个节点审批人和审批组
  • 利用Spring Boot实现微服务的API限流策略
  • 用于基于骨架的动作识别的空间时间图卷积网络 ST-GCN (代码+数据集+模型)
  • window.localStorage 与 window.sessionStorage的区别
  • PTA单词首字母大写
  • opencv学习时常用linux命令
  • [Java]SpringBoot业务代码增强
  • AI-Talk开发板之helloword
  • Python OpenCV 深入理解(二)
  • Arduino简介
  • 【C++题解】1002 - 编程求解1+2+3+...+n
  • 通过任务建立职业自信
  • 万界星空科技云MES系统:提升生产效率与质量
  • C++ | Leetcode C++题解之第393题UFT-8编码验证
  • Python | Leetcode Python题解之第393题UTF-8编码验证
  • 如何选择SSD
  • IBM企业流程框架方法论-附PPT下载
  • 查看端口被占用情况