应用层
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 )} 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 Parcelable
public static class FmBandDescriptor extends BandDescriptor
public static class AmBandDescriptor extends BandDescriptor
...
public static class BandConfig implements Parcelable
public static class FmBandConfig extends BandConfig
public 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");
}
}
@Override
public List listModules() {
enforcePolicyAccess();
synchronized (mLock) {
...
mModules = nativeLoadModules(mNativeContext);
...
return mModules;
}
}
@Override
public 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& 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 BroadcastRadio::openTuner(const BandConfig& config, bool audio,
const sp& callback, openTuner_cb _hidl_cb)
{
sp 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;
...
//这里以这个常见的调频函数举例
@Override
public void tune(@NonNull ProgramSelector selector) {
try {
mTuner.tune(selector);
} catch (RemoteException e) {
throw new RuntimeException("service died", e);
}
}
//收音机还有图片?什么图片?频道logo???
@Override
public @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 {
...
//接着用调频函数举例
@Override
public 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 Tuner::tune(uint32_t channel, uint32_t subChannel) {
ALOGV("%s(%d, %d)", __func__, channel, subChannel);
Band band;
{
lock_guard lk(mMut);
band = mAmfmConfig.type;
}
return tuneByProgramSelector(utils::make_selector(band, channel, subChannel));
}
Return Tuner::tuneByProgramSelector(const ProgramSelector& sel) {
...
}
而
hardware/interfaces/broadcastradio/1.0/default/Tuner.cpp
Return 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!