【Android源码分析】深入理解Radio(Tunner)创建分析

这篇文章主要根据代码结构从JAVA层到JNI层再到HAL层理解分析Radio的创建流程分解:
说明Radio和Tunner都是指的一个意思,后面叙述就用Radio描述。

1.JAVA层创建Radio(Tunner)

1.1RadioManager中调用openTuner:

------------------------------------------------------------------------------------------------------------------------------------------
/**
 * Opens the current radio band. Currently, this only supports FM and AM bands.
 *
 * @param radioBand One of {@link RadioManager#BAND_FM}, {@link RadioManager#BAND_AM},
 *                  {@link RadioManager#BAND_FM_HD} or {@link RadioManager#BAND_AM_HD}.
 * @return {@link RadioManager#STATUS_OK} if successful; otherwise,
 * {@link RadioManager#STATUS_ERROR}.
 */
private int openRadioBandInternal(int radioBand) {
    if (requestAudioFocus() != AudioManager.AUDIOFOCUS_REQUEST_GRANTED) {
        Log.e(TAG, "openRadioBandInternal() audio focus request fail");
        return RadioManager.STATUS_ERROR;
    }
    //mCurrentRadioBand = radioBand;
    RadioManager.BandConfig config = getRadioConfig(radioBand);
    if (config == null) {
        Log.w(TAG, "Cannot create config for radio band: " + radioBand);
        return RadioManager.STATUS_ERROR;
    }
    Log.e("Radio", "[RadioService] openRadioBandInternal " + config);
    if (mRadioTuner != null) {
        mRadioTuner.setConfiguration(config);
    } else {
        mRadioTuner = mRadioManager.openTuner(mModules.get(0).getId(), config, true,
                                              mInternalRadioTunerCallback, null /* handler */);

    }
    if (Log.isLoggable(TAG, Log.DEBUG)) {
        Log.d(TAG, "openRadioBandInternal() STATUS_OK");
    }
    // Reset the counter for exponential backoff each time the radio tuner has been successfully
    // opened.
    mReOpenRadioTunerCount = 0;
    return RadioManager.STATUS_OK;
}
------------------------------------------------------------------------------------------------------------------------------------------
分析:openTuner函数是作用就是创建Radio功能。其中
id就是底层设备ID ModuleProperties对应的是HAL层底层设备参数;
config就是Radio的band配置信息;
mInternalRadioTunerCallback 回调函数

------------------------------------------------------------------------------------------------------------------------------------------

1.2 RadioTuner 中调用openTuner

------------------------------------------------------------------------------------------------------------------------------------------
public RadioTuner openTuner(int moduleId, RadioManager.BandConfig config, boolean withAudio, Callback callback, Handler handler) {
    if (callback == null) {
        return null;
    } else {
        RadioModule module = new RadioModule(moduleId, config, withAudio, callback, handler);
        if (module != null && !module.initCheck()) {
            module = null;
        }


        return module;
    }
}
------------------------------------------------------------------------------------------------------------------------------------------

1.3 RadioModule 中真正调用JNI函数native_setup来创建

------------------------------------------------------------------------------------------------------------------------------------------
RadioModule(int moduleId, BandConfig config, boolean withAudio, Callback callback, Handler handler) {
    this.mId = moduleId;
    this.mEventHandlerDelegate = new RadioModule.NativeEventHandlerDelegate(callback, handler);
    this. native_setup(new WeakReference(this), config, withAudio);
}
------------------------------------------------------------------------------------------------------------------------------------------

2.JNI层分析

2.1  RadioModule中的 native_setup定义

------------------------------------------------------------------------------------------------------------------------------------------
public class RadioModule extends RadioTuner {
    private long mNativeContext = 0;
    private int mId;
    private NativeEventHandlerDelegate mEventHandlerDelegate;


    RadioModule(int moduleId, RadioManager.BandConfig config, boolean withAudio,
            RadioTuner.Callback callback, Handler handler) {
        mId = moduleId;
        mEventHandlerDelegate = new NativeEventHandlerDelegate(callback, handler);
        native_setup(new WeakReference<RadioModule>(this), config, withAudio);
    }
    private native void native_setup(Object module_this,
            RadioManager.BandConfig config, boolean withAudio);
static JNINativeMethod gModuleMethods[] = {
    {"native_setup",
        "(Ljava/lang/Object;Landroid/hardware/radio/RadioManager$BandConfig;Z)V",
        (void *)android_hardware_Radio_setup},
};
------------------------------------------------------------------------------------------------------------------------------------------

2.2 JNI文件中声明和定义 android_hardware_Radio.cpp 

2.2.1 声明

------------------------------------------------------------------------------------------------------------------------------------------
static JNINativeMethod gModuleMethods[] = {
    {" native_setup",
        "(Ljava/lang/Object;Landroid/hardware/radio/RadioManager$BandConfig;Z)V",
        (void *) android_hardware_Radio_setup},
...
};
------------------------------------------------------------------------------------------------------------------------------------------

2.2.2 定义

------------------------------------------------------------------------------------------------------------------------------------------
  static void
android_hardware_Radio_setup(JNIEnv *env, jobject thiz,
                             jobject weak_this, jobject jConfig, jboolean withAudio)
{
    ALOGV("%s", __FUNCTION__);


    setRadio(env, thiz, 0);


    sp<JNIRadioCallback> callback = new JNIRadioCallback(env, thiz, weak_this);


    radio_handle_t handle = (radio_handle_t)env->GetIntField(thiz, gModuleFields.mId);


    struct radio_band_config nConfig;
    struct radio_band_config *configPtr = NULL;
    if (jConfig != NULL) {
        jint jStatus = convertBandConfigToNative(env, &nConfig, jConfig);
        if (jStatus != RADIO_STATUS_OK) {
            return;
        }
        configPtr = &nConfig;
    }
    sp<Radio> module = Radio::attach(handle, configPtr, (bool)withAudio, callback);
    if (module == 0) {
        return;
    }
    setRadio(env, thiz, module);
}
其中attach函数作为JNI到HAL层的发起点
------------------------------------------------------------------------------------------------------------------------------------------

2.2.3 Radio(framework\av\radio)中调用RadioServiceattach

------------------------------------------------------------------------------------------------------------------------------------------
sp<Radio> Radio::attach(radio_handle_t handle,
                        const struct radio_band_config *config,
                        bool withAudio,
                        const sp<RadioCallback>& callback)
{
    ALOGV("attach()");
    sp<Radio> radio;
    const sp<IRadioService> service = getRadioService();
    if (service == 0) {
        return radio;
    }
    radio = new Radio(handle, callback);
    status_t status = service->attach(handle, radio, config, withAudio, radio->mIRadio);


    if (status == NO_ERROR && radio->mIRadio != 0) {
        IInterface::asBinder(radio->mIRadio)->linkToDeath(radio);
    } else {
        ALOGW("Error %d connecting to radio service", status);
        radio.clear();
    }
    return radio;
}
------------------------------------------------------------------------------------------------------------------------------------------

2.2.4 RadioService(framework\av\service\radio)

------------------------------------------------------------------------------------------------------------------------------------------
status_t RadioService::attach(radio_handle_t handle,
                        const sp<IRadioClient>& client,
                        const struct radio_band_config *config,
                        bool withAudio,
                        sp<IRadio>& radio)
{
    ALOGV("%s %d config %p withAudio %d", __FUNCTION__, handle, config, withAudio);


    AutoMutex lock(mServiceLock);
    radio.clear();
    if (client == 0) {
        return BAD_VALUE;
    }
    ssize_t index = mModules.indexOfKey(handle);
    if (index < 0) {
        return BAD_VALUE;
    }
    sp<Module> module = mModules.valueAt(index);


    if (config == NULL) {
        config = module->getDefaultConfig(); // ①
        if (config == NULL) {
            return INVALID_OPERATION;
        }
    }
    ALOGV("%s region %d type %d", __FUNCTION__, config->region, config->band.type);


    radio = module->addClient(client, config, withAudio); // ②


    if (radio == 0) {
        return NO_INIT;
    }
    return NO_ERROR;
}
①处:获取对应国家band信息,比如最大FM值;最小FM值等。
②处:通过传入参数创建radio实例

------------------------------------------------------------------------------------------------------------------------------------------

2.2.4.1 介绍 getDefaultConfig 中band参数的来源

const struct radio_band_config *RadioService::Module::getDefaultConfig() const
{
    if (mProperties.num_bands == 0) {
        return NULL;
    }
    return &mProperties.bands[0];
}
mProperties来自下面的设备halProperties参数
------------------------------------------------------------------------------------------------------------------------------------------
void RadioService::onFirstRef()
{
    const hw_module_t *mod;
    int rc;
    struct radio_hw_device *dev;


    ALOGI("%s", __FUNCTION__);


    rc = hw_get_module_by_class(RADIO_HARDWARE_MODULE_ID, RADIO_HARDWARE_MODULE_ID_FM, &mod);
    if (rc != 0) {
        ALOGE("couldn't load radio module %s.%s (%s)",
              RADIO_HARDWARE_MODULE_ID, "primary", strerror(-rc));
        return;
    }
    rc = radio_hw_device_open(mod, &dev);
    if (rc != 0) {
        ALOGE("couldn't open radio hw device in %s.%s (%s)",
              RADIO_HARDWARE_MODULE_ID, "primary", strerror(-rc));
        return;
    }
    if (dev->common.version != RADIO_DEVICE_API_VERSION_CURRENT) {
        ALOGE("wrong radio hw device version %04x", dev->common.version);
        return;
    }


    struct radio_hal_properties halProperties;
    rc = dev->get_properties(dev, &halProperties);  // 调用底层HAL函数  rdev_get_properties 获取当前设备信息,看下面HAL分析
    if (rc != 0) {
        ALOGE("could not read implementation properties");
        return;
    }


    radio_properties_t properties;
    properties.handle =
            (radio_handle_t)android_atomic_inc(&mNextUniqueId);


    ALOGI("loaded default module %s, handle %d", properties.product, properties.handle);


    convertProperties(&properties, &halProperties); // 转换拷贝band信息 
    sp<Module> module = new Module(dev, properties);
    mModules.add(properties.handle, module);
}
说明其中在convertProperties函数中就调用的各个国家的信息赋值 sKnownRegionConfigs,如下:
------------------------------------------------------------------------------------------------------------------------------------------
/* static */
void RadioService:: convertProperties(radio_properties_t *properties,
                                     const radio_hal_properties_t *halProperties)
{
    memset(properties, 0, sizeof(struct radio_properties));
    properties->class_id = halProperties->class_id;
    strlcpy(properties->implementor, halProperties->implementor,
            RADIO_STRING_LEN_MAX);
    strlcpy(properties->product, halProperties->product,
            RADIO_STRING_LEN_MAX);
    strlcpy(properties->version, halProperties->version,
            RADIO_STRING_LEN_MAX);
    strlcpy(properties->serial, halProperties->serial,
            RADIO_STRING_LEN_MAX);
    properties->num_tuners = halProperties->num_tuners;
    properties->num_audio_sources = halProperties->num_audio_sources;
    properties->supports_capture = halProperties->supports_capture;


    for (size_t i = 0; i < ARRAY_SIZE(sKnownRegionConfigs); i++) {
        const radio_hal_band_config_t *band = & sKnownRegionConfigs[i].band;
        size_t j;
        for (j = 0; j < halProperties->num_bands; j++) {
            const radio_hal_band_config_t *halBand = &halProperties->bands[j];
            size_t k;
            if (band->type != halBand->type) continue;
            if (band->lower_limit < halBand->lower_limit) continue;
            if (band->upper_limit > halBand->upper_limit) continue;
            for (k = 0; k < halBand->num_spacings; k++) {
                if (band->spacings[0] == halBand->spacings[k]) break;
            }
            if (k == halBand->num_spacings) continue;
            if (band->type == RADIO_BAND_AM) break;
            if ((band->fm.deemphasis & halBand->fm.deemphasis) == 0) continue;
            if (halBand->fm.rds == 0) break;
            if ((band->fm.rds & halBand->fm.rds) != 0) break;
        }
        if (j == halProperties->num_bands) continue;


        ALOGI("convertProperties() Adding band type %d region %d",
              sKnownRegionConfigs[i].band.type , sKnownRegionConfigs[i].region);


        memcpy(&properties->bands[properties->num_bands++],
               &sKnownRegionConfigs[i],
               sizeof(radio_band_config_t));
    }
}
------------------------------------------------------------------------------------------------------------------------------------------

3.HAL层代码调用 获取设备信息

------------------------------------------------------------------------------------------------------------------------------------------

3.1关系代码在Radio_hw.c中,HAL层函数接口绑定如下:

void RadioService::onFirstRef()中已经看到代码
rc = dev->get_properties(dev, &halProperties); 

------------------------------------------------------------------------------------------------------------------------------------------
code:
static int rdev_open(const hw_module_t *module,
                     const char *name,
                     hw_device_t **device) {
    struct stub_radio_device *rdev;
    int ret;


    if (strcmp(name, RADIO_HARDWARE_DEVICE) != 0) {
        return -EINVAL;
    }


    rdev = calloc(1,
                  sizeof(struct stub_radio_device));
    if (!rdev) {
        return -ENOMEM;
    }


    rdev->device.common.tag = HARDWARE_DEVICE_TAG;
    rdev->device.common.version =
        RADIO_DEVICE_API_VERSION_1_0;
    rdev->device.common.module = (struct hw_module_t *) module;
    rdev->device.common.close = rdev_close;
    rdev->device.get_properties = rdev_get_properties;
    rdev->device.open_tuner = rdev_open_tuner;
    rdev->device.close_tuner = rdev_close_tuner;


    pthread_mutex_init(&rdev->lock, (const pthread_mutexattr_t *) NULL);


    *device = &rdev->device.common;


    return 0;
}
------------------------------------------------------------------------------------------------------------------------------------------

3.2最终HAL层调用rdev_get_properties 获取设备信息

static int rdev_get_properties(const struct
                               radio_hw_device *dev,
                               radio_hal_properties_t *properties) {
    struct stub_radio_device *rdev = (struct
                                      stub_radio_device *)dev;


    ALOGI("%s", __func__);
    if (properties == NULL) {
        return -EINVAL;
    }
    memcpy(properties, &hw_properties,
           sizeof(radio_hal_properties_t));
    return 0;
}
------------------------------------------------------------------------------------------------------------------------------------------

3.3 hw_properties定义如下

static const radio_hal_properties_t hw_properties = {
    .class_id = RADIO_CLASS_AM_FM,
    .implementor = "The Android Open Source Project",
    .product = "Radio stub HAL",
    .version = "0.1",
    .serial = "0123456789",
    .num_tuners = 1,
    .num_audio_sources = 1,
    .supports_capture = false,
    .num_bands = 2,
    .bands = {
        {
            .type = RADIO_BAND_FM,
            .antenna_connected = true,
            .lower_limit = 87500,
            .upper_limit = 108000,
            .num_spacings = 1,
            .spacings = { 100 },
            .fm = {
                .deemphasis = RADIO_DEEMPHASIS_50,
                .stereo = true,
                .rds = RADIO_RDS_NONE,
                .ta = false,
                .af = false,
                .ea = false,
            }
        },
        {
            .type = RADIO_BAND_AM,
            .antenna_connected = true,
            .lower_limit = 531,
            .upper_limit = 1611,
            .num_spacings = 1,
            .spacings = { 9 },
            .am = {
                .stereo = true,
            }
        }
    }
};
该结构体定义了Radio基本信息包括band初始信息
注意此变量只会在radio设备初始化时候调用一次,如果需要更改不同国家不同band信息配置,那么需要在openTuner中传入对应国家band
配置信息即可

------------------------------------------------------------------------------------------------------------------------------------------

猜你喜欢

转载自blog.csdn.net/xuqiang918/article/details/80065298