【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
配置信息即可

——————————————————————————————————————————————

    原文作者:Android源码分析
    原文地址: https://blog.csdn.net/xuqiang918/article/details/80065298
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞