Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 8af409bc authored by TreeHugger Robot's avatar TreeHugger Robot Committed by Android (Google) Code Review
Browse files

Merge "broadcast radio: initial support for HIDL HAL"

parents 1adf7f84 9347a771
Loading
Loading
Loading
Loading
+21 −3
Original line number Diff line number Diff line
@@ -18,8 +18,7 @@ include $(CLEAR_VARS)


LOCAL_SRC_FILES:= \
    RadioService.cpp \
    RadioHalLegacy.cpp
    RadioService.cpp

LOCAL_SHARED_LIBRARIES:= \
    liblog \
@@ -31,6 +30,25 @@ LOCAL_SHARED_LIBRARIES:= \
    libradio \
    libradio_metadata

ifeq ($(ENABLE_TREBLE),true)
# Treble configuration
LOCAL_CFLAGS += -DENABLE_TREBLE
LOCAL_SRC_FILES += \
    HidlUtils.cpp \
    RadioHalHidl.cpp

LOCAL_SHARED_LIBRARIES += \
    libhwbinder \
    libhidl \
    libbase \
    android.hardware.broadcastradio@1.0
else
# libhardware configuration
LOCAL_SRC_FILES +=               \
    RadioHalLegacy.cpp
endif


LOCAL_CFLAGS += -Wall -Wextra -Werror

LOCAL_MULTILIB := $(AUDIOSERVER_MULTILIB)
+179 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2016 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
#define LOG_TAG "HidlUtils"
//#define LOG_NDEBUG 0

#include <utils/Log.h>
#include <utils/misc.h>
#include <system/radio_metadata.h>

#include "HidlUtils.h"

namespace android {

using android::hardware::broadcastradio::V1_0::MetadataType;
using android::hardware::broadcastradio::V1_0::Band;
using android::hardware::broadcastradio::V1_0::Deemphasis;
using android::hardware::broadcastradio::V1_0::Rds;

//static
int HidlUtils::convertHalResult(Result result)
{
    switch (result) {
        case Result::OK:
            return 0;
        case Result::INVALID_ARGUMENTS:
            return -EINVAL;
        case Result::INVALID_STATE:
            return -ENOSYS;
        case Result::TIMEOUT:
            return -ETIMEDOUT;
        case Result::NOT_INITIALIZED:
        default:
            return -ENODEV;
    }
}


//static
void HidlUtils::convertBandConfigToHal(BandConfig *halConfig,
                                       const radio_hal_band_config_t *config)
{
    halConfig->type = static_cast<Band>(config->type);
    halConfig->antennaConnected = config->antenna_connected;
    halConfig->lowerLimit = config->lower_limit;
    halConfig->upperLimit = config->upper_limit;
    halConfig->spacings.setToExternal(const_cast<unsigned int *>(&config->spacings[0]),
                                       config->num_spacings * sizeof(uint32_t));
    // FIXME: transfer buffer ownership. should have a method for that in hidl_vec
    halConfig->spacings.resize(config->num_spacings);

    if (halConfig->type == Band::FM) {
        halConfig->ext.fm.deemphasis = static_cast<Deemphasis>(config->fm.deemphasis);
        halConfig->ext.fm.stereo = config->fm.stereo;
        halConfig->ext.fm.rds = static_cast<Rds>(config->fm.rds);
        halConfig->ext.fm.ta = config->fm.ta;
        halConfig->ext.fm.af = config->fm.af;
        halConfig->ext.fm.ea = config->fm.ea;
    } else {
        halConfig->ext.am.stereo = config->am.stereo;
    }
}

//static
void HidlUtils::convertPropertiesFromHal(radio_hal_properties_t *properties,
                                         const Properties *halProperties)
{
    properties->class_id = static_cast<radio_class_t>(halProperties->classId);
    strlcpy(properties->implementor, halProperties->implementor.c_str(), RADIO_STRING_LEN_MAX);
    strlcpy(properties->product, halProperties->product.c_str(), RADIO_STRING_LEN_MAX);
    strlcpy(properties->version, halProperties->version.c_str(), RADIO_STRING_LEN_MAX);
    strlcpy(properties->serial, halProperties->serial.c_str(), RADIO_STRING_LEN_MAX);
    properties->num_tuners = halProperties->numTuners;
    properties->num_audio_sources = halProperties->numAudioSources;
    properties->supports_capture = halProperties->supportsCapture;
    properties->num_bands = halProperties->bands.size();

    for (size_t i = 0; i < halProperties->bands.size(); i++) {
        convertBandConfigFromHal(&properties->bands[i], &halProperties->bands[i]);
    }
}

//static
void HidlUtils::convertBandConfigFromHal(radio_hal_band_config_t *config,
                                         const BandConfig *halConfig)
{
    config->type = static_cast<radio_band_t>(halConfig->type);
    config->antenna_connected = halConfig->antennaConnected;
    config->lower_limit = halConfig->lowerLimit;
    config->upper_limit = halConfig->upperLimit;
    config->num_spacings = halConfig->spacings.size();
    if (config->num_spacings > RADIO_NUM_SPACINGS_MAX) {
        config->num_spacings = RADIO_NUM_SPACINGS_MAX;
    }
    memcpy(config->spacings, halConfig->spacings.data(),
           sizeof(uint32_t) * config->num_spacings);

    if (halConfig->type == Band::FM) {
        config->fm.deemphasis = static_cast<radio_deemphasis_t>(halConfig->ext.fm.deemphasis);
        config->fm.stereo = halConfig->ext.fm.stereo;
        config->fm.rds = static_cast<radio_rds_t>(halConfig->ext.fm.rds);
        config->fm.ta = halConfig->ext.fm.ta;
        config->fm.af = halConfig->ext.fm.af;
        config->fm.ea = halConfig->ext.fm.ea;
    } else {
        config->am.stereo = halConfig->ext.am.stereo;
    }
}


//static
void HidlUtils::convertProgramInfoFromHal(radio_program_info_t *info,
                                          const ProgramInfo *halInfo,
                                          bool withMetadata)
{
    info->channel = halInfo->channel;
    info->sub_channel = halInfo->subChannel;
    info->tuned = halInfo->tuned;
    info->stereo = halInfo->stereo;
    info->digital = halInfo->digital;
    info->signal_strength = halInfo->signalStrength;
    if (withMetadata && halInfo->metadata.size() != 0) {
        convertMetaDataFromHal(&info->metadata, halInfo->metadata,
                               halInfo->channel, halInfo->subChannel);
    }
}

//static
void HidlUtils::convertMetaDataFromHal(radio_metadata_t **metadata,
                                       const hidl_vec<MetaData>& halMetadata,
                                       uint32_t channel,
                                       uint32_t subChannel)
{

    radio_metadata_allocate(metadata, channel, subChannel);
    for (size_t i = 0; i < halMetadata.size(); i++) {
        radio_metadata_key_t key = static_cast<radio_metadata_key_t>(halMetadata[i].key);
        radio_metadata_type_t type = static_cast<radio_metadata_key_t>(halMetadata[i].type);
        radio_metadata_clock_t clock;

        switch (type) {
        case RADIO_METADATA_TYPE_INT:
            radio_metadata_add_int(metadata, key, halMetadata[i].intValue);
            break;
        case RADIO_METADATA_TYPE_TEXT:
            radio_metadata_add_text(metadata, key, halMetadata[i].stringValue.c_str());
            break;
        case RADIO_METADATA_TYPE_RAW:
            radio_metadata_add_raw(metadata, key,
                                   halMetadata[i].rawValue.data(),
                                   halMetadata[i].rawValue.size());
            break;
        case RADIO_METADATA_TYPE_CLOCK:
            clock.utc_seconds_since_epoch =
                    halMetadata[i].clockValue.utcSecondsSinceEpoch;
            clock.timezone_offset_in_minutes =
                    halMetadata[i].clockValue.timezoneOffsetInMinutes;
            radio_metadata_add_clock(metadata, key, &clock);
            break;
        default:
            ALOGW("%s invalid metadata type %u",__FUNCTION__, halMetadata[i].type);
            break;
        }
    }
}

}  // namespace android
+51 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2016 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
#ifndef ANDROID_HARDWARE_RADIO_HAL_HIDL_UTILS_H
#define ANDROID_HARDWARE_RADIO_HAL_HIDL_UTILS_H

#include <android/hardware/broadcastradio/1.0/types.h>
#include <hardware/radio.h>

namespace android {

using android::hardware::hidl_vec;
using android::hardware::broadcastradio::V1_0::Result;
using android::hardware::broadcastradio::V1_0::Properties;
using android::hardware::broadcastradio::V1_0::BandConfig;
using android::hardware::broadcastradio::V1_0::ProgramInfo;
using android::hardware::broadcastradio::V1_0::MetaData;

class HidlUtils {
public:
    static int convertHalResult(Result result);
    static void convertBandConfigFromHal(radio_hal_band_config_t *config,
                                         const BandConfig *halConfig);
    static void convertPropertiesFromHal(radio_hal_properties_t *properties,
                                         const Properties *halProperties);
    static void convertBandConfigToHal(BandConfig *halConfig,
                                       const radio_hal_band_config_t *config);
    static void convertProgramInfoFromHal(radio_program_info_t *info,
                                          const ProgramInfo *halInfo,
                                          bool withMetadata);
    static void convertMetaDataFromHal(radio_metadata_t **metadata,
                                       const hidl_vec<MetaData>& halMetadata,
                                       uint32_t channel,
                                       uint32_t subChannel);
};

}  // namespace android

#endif  // ANDROID_HARDWARE_RADIO_HAL_HIDL_UTILS_H
+388 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2016 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#define LOG_TAG "RadioHalHidl"
//#define LOG_NDEBUG 0

#include <utils/Log.h>
#include <utils/misc.h>
#include <system/radio_metadata.h>
#include <android/hardware/broadcastradio/1.0/IBroadcastRadioFactory.h>

#include "RadioHalHidl.h"
#include "HidlUtils.h"

namespace android {

using android::hardware::broadcastradio::V1_0::IBroadcastRadioFactory;
using android::hardware::broadcastradio::V1_0::Class;
using android::hardware::broadcastradio::V1_0::Direction;
using android::hardware::broadcastradio::V1_0::Properties;


/* static */
sp<RadioInterface> RadioInterface::connectModule(radio_class_t classId)
{
    return new RadioHalHidl(classId);
}

int RadioHalHidl::getProperties(radio_hal_properties_t *properties)
{
    ALOGV("%s IN", __FUNCTION__);
    sp<IBroadcastRadio> module = getService();
    if (module == 0) {
        return -ENODEV;
    }
    Properties halProperties;
    Result halResult;
    Return<void> hidlReturn =
            module->getProperties([&](Result result, const Properties& properties) {
                    halResult = result;
                    if (result == Result::OK) {
                        halProperties = properties;
                    }
                });

    if (hidlReturn.getStatus().transactionError() == DEAD_OBJECT) {
        clearService();
        return -EPIPE;
    }
    if (halResult == Result::OK) {
        HidlUtils::convertPropertiesFromHal(properties, &halProperties);
    }
    return HidlUtils::convertHalResult(halResult);
}

int RadioHalHidl::openTuner(const radio_hal_band_config_t *config,
                            bool audio,
                            sp<TunerCallbackInterface> callback,
                            sp<TunerInterface>& tuner)
{
    sp<IBroadcastRadio> module = getService();
    if (module == 0) {
        return -ENODEV;
    }
    sp<Tuner> tunerImpl = new Tuner(callback, this);

    BandConfig halConfig;
    Result halResult;
    sp<ITuner> halTuner;

    HidlUtils::convertBandConfigToHal(&halConfig, config);
    Return<void> hidlReturn =
            module->openTuner(halConfig, audio, tunerImpl,
                              [&](Result result, const sp<ITuner>& tuner) {
                    halResult = result;
                    if (result == Result::OK) {
                        halTuner = tuner;
                    }
                });

    if (hidlReturn.getStatus().transactionError() == DEAD_OBJECT) {
        clearService();
        return -EPIPE;
    }
    if (halResult == Result::OK) {
        tunerImpl->setHalTuner(halTuner);
        tuner = tunerImpl;
    }

    return HidlUtils::convertHalResult(halResult);
}

int RadioHalHidl::closeTuner(sp<TunerInterface>& tuner)
{
    sp<Tuner> tunerImpl = static_cast<Tuner *>(tuner.get());
    sp<ITuner> clearTuner;
    tunerImpl->setHalTuner(clearTuner);
    return 0;
}

RadioHalHidl::RadioHalHidl(radio_class_t classId)
    : mClassId(classId)
{
}

RadioHalHidl::~RadioHalHidl()
{
}

sp<IBroadcastRadio> RadioHalHidl::getService()
{
    if (mHalModule == 0) {
        sp<IBroadcastRadioFactory> factory = IBroadcastRadioFactory::getService("broadcastradio");
        if (factory != 0) {
            factory->connectModule(static_cast<Class>(mClassId),
                               [&](Result retval, const ::android::sp<IBroadcastRadio>& result) {
                if (retval == Result::OK) {
                    mHalModule = result;
                }
            });
        }
    }
    ALOGV("%s OUT module %p", __FUNCTION__, mHalModule.get());
    return mHalModule;
}

void RadioHalHidl::clearService()
{
    ALOGV("%s IN module %p", __FUNCTION__, mHalModule.get());
    mHalModule.clear();
}


int RadioHalHidl::Tuner::setConfiguration(const radio_hal_band_config_t *config)
{
    ALOGV("%s IN mHalTuner %p", __FUNCTION__, mHalTuner.get());

    if (mHalTuner == 0) {
        return -ENODEV;
    }
    BandConfig halConfig;
    HidlUtils::convertBandConfigToHal(&halConfig, config);

    Return<Result> hidlResult = mHalTuner->setConfiguration(halConfig);
    checkHidlStatus(hidlResult.getStatus());
    return HidlUtils::convertHalResult(hidlResult);
}

int RadioHalHidl::Tuner::getConfiguration(radio_hal_band_config_t *config)
{
    ALOGV("%s IN mHalTuner %p", __FUNCTION__, mHalTuner.get());
    if (mHalTuner == 0) {
        return -ENODEV;
    }
    BandConfig halConfig;
    Result halResult;
    Return<void> hidlReturn =
            mHalTuner->getConfiguration([&](Result result, const BandConfig& config) {
                    halResult = result;
                    if (result == Result::OK) {
                        halConfig = config;
                    }
                });
    status_t status = checkHidlStatus(hidlReturn.getStatus());
    if (status == NO_ERROR && halResult == Result::OK) {
        HidlUtils::convertBandConfigFromHal(config, &halConfig);
    }
    return HidlUtils::convertHalResult(halResult);
}

int RadioHalHidl::Tuner::scan(radio_direction_t direction, bool skip_sub_channel)
{
    ALOGV("%s IN mHalTuner %p", __FUNCTION__, mHalTuner.get());
    if (mHalTuner == 0) {
        return -ENODEV;
    }
    Return<Result> hidlResult =
            mHalTuner->scan(static_cast<Direction>(direction), skip_sub_channel);
    checkHidlStatus(hidlResult.getStatus());
    return HidlUtils::convertHalResult(hidlResult);
}

int RadioHalHidl::Tuner::step(radio_direction_t direction, bool skip_sub_channel)
{
    ALOGV("%s IN mHalTuner %p", __FUNCTION__, mHalTuner.get());
    if (mHalTuner == 0) {
        return -ENODEV;
    }
    Return<Result> hidlResult =
            mHalTuner->step(static_cast<Direction>(direction), skip_sub_channel);
    checkHidlStatus(hidlResult.getStatus());
    return HidlUtils::convertHalResult(hidlResult);
}

int RadioHalHidl::Tuner::tune(unsigned int channel, unsigned int sub_channel)
{
    ALOGV("%s IN mHalTuner %p", __FUNCTION__, mHalTuner.get());
    if (mHalTuner == 0) {
        return -ENODEV;
    }
    Return<Result> hidlResult =
            mHalTuner->tune(channel, sub_channel);
    checkHidlStatus(hidlResult.getStatus());
    return HidlUtils::convertHalResult(hidlResult);
}

int RadioHalHidl::Tuner::cancel()
{
    ALOGV("%s IN mHalTuner %p", __FUNCTION__, mHalTuner.get());
    if (mHalTuner == 0) {
        return -ENODEV;
    }
    Return<Result> hidlResult = mHalTuner->cancel();
    checkHidlStatus(hidlResult.getStatus());
    return HidlUtils::convertHalResult(hidlResult);
}

int RadioHalHidl::Tuner::getProgramInformation(radio_program_info_t *info)
{
    ALOGV("%s IN mHalTuner %p", __FUNCTION__, mHalTuner.get());
    if (mHalTuner == 0) {
        return -ENODEV;
    }
    ProgramInfo halInfo;
    Result halResult;
    bool withMetaData = (info->metadata != NULL);
    Return<void> hidlReturn = mHalTuner->getProgramInformation(
                    withMetaData, [&](Result result, const ProgramInfo& info) {
                        halResult = result;
                        if (result == Result::OK) {
                            halInfo = info;
                        }
    });
    status_t status = checkHidlStatus(hidlReturn.getStatus());
    if (status == NO_ERROR && halResult == Result::OK) {
        HidlUtils::convertProgramInfoFromHal(info, &halInfo, withMetaData);
    }
    return HidlUtils::convertHalResult(halResult);
}

Return<void> RadioHalHidl::Tuner::hardwareFailure()
{
    ALOGV("%s IN", __FUNCTION__);
    handleHwFailure();
    return Return<void>();
}

Return<void> RadioHalHidl::Tuner::configChange(Result result, const BandConfig& config)
{
    ALOGV("%s IN", __FUNCTION__);
    radio_hal_event_t event;
    memset(&event, 0, sizeof(radio_hal_event_t));
    event.type = RADIO_EVENT_CONFIG;
    event.status = HidlUtils::convertHalResult(result);
    HidlUtils::convertBandConfigFromHal(&event.config, &config);
    onCallback(&event);
    return Return<void>();
}

Return<void> RadioHalHidl::Tuner::tuneComplete(Result result, const ProgramInfo& info)
{
    ALOGV("%s IN", __FUNCTION__);
    radio_hal_event_t event;
    memset(&event, 0, sizeof(radio_hal_event_t));
    event.type = RADIO_EVENT_TUNED;
    event.status = HidlUtils::convertHalResult(result);
    HidlUtils::convertProgramInfoFromHal(&event.info, &info, true);
    onCallback(&event);
    if (event.info.metadata != NULL) {
        radio_metadata_deallocate(event.info.metadata);
    }
    return Return<void>();
}

Return<void> RadioHalHidl::Tuner::afSwitch(const ProgramInfo& info)
{
    ALOGV("%s IN", __FUNCTION__);
    radio_hal_event_t event;
    memset(&event, 0, sizeof(radio_hal_event_t));
    event.type = RADIO_EVENT_AF_SWITCH;
    HidlUtils::convertProgramInfoFromHal(&event.info, &info, true);
    onCallback(&event);
    if (event.info.metadata != NULL) {
        radio_metadata_deallocate(event.info.metadata);
    }
    return Return<void>();
}

Return<void> RadioHalHidl::Tuner::antennaStateChange(bool connected)
{
    ALOGV("%s IN", __FUNCTION__);
    radio_hal_event_t event;
    memset(&event, 0, sizeof(radio_hal_event_t));
    event.type = RADIO_EVENT_ANTENNA;
    event.on = connected;
    onCallback(&event);
    return Return<void>();
}
Return<void> RadioHalHidl::Tuner::trafficAnnouncement(bool active)
{
    ALOGV("%s IN", __FUNCTION__);
    radio_hal_event_t event;
    memset(&event, 0, sizeof(radio_hal_event_t));
    event.type = RADIO_EVENT_TA;
    event.on = active;
    onCallback(&event);
    return Return<void>();
}
Return<void> RadioHalHidl::Tuner::emergencyAnnouncement(bool active)
{
    ALOGV("%s IN", __FUNCTION__);
    radio_hal_event_t event;
    memset(&event, 0, sizeof(radio_hal_event_t));
    event.type = RADIO_EVENT_EA;
    event.on = active;
    onCallback(&event);
    return Return<void>();
}
Return<void> RadioHalHidl::Tuner::newMetadata(uint32_t channel, uint32_t subChannel,
                                          const ::android::hardware::hidl_vec<MetaData>& metadata)
{
    ALOGV("%s IN", __FUNCTION__);
    radio_hal_event_t event;
    memset(&event, 0, sizeof(radio_hal_event_t));
    event.type = RADIO_EVENT_METADATA;
    HidlUtils::convertMetaDataFromHal(&event.metadata, metadata, channel, subChannel);
    onCallback(&event);
    if (event.metadata != NULL) {
        radio_metadata_deallocate(event.info.metadata);
    }
    return Return<void>();
}


RadioHalHidl::Tuner::Tuner(sp<TunerCallbackInterface> callback, sp<RadioHalHidl> module)
    : TunerInterface(), mHalTuner(NULL), mCallback(callback), mParentModule(module)
{
}


RadioHalHidl::Tuner::~Tuner()
{
}

void RadioHalHidl::Tuner::handleHwFailure()
{
    ALOGV("%s IN", __FUNCTION__);
    sp<RadioHalHidl> parentModule = mParentModule.promote();
    if (parentModule != 0) {
        parentModule->clearService();
    }
    radio_hal_event_t event;
    memset(&event, 0, sizeof(radio_hal_event_t));
    event.type = RADIO_EVENT_HW_FAILURE;
    onCallback(&event);
    mHalTuner.clear();
}

status_t RadioHalHidl::Tuner::checkHidlStatus(Status hidlStatus)
{
    status_t status = hidlStatus.transactionError();
    if (status == DEAD_OBJECT) {
        handleHwFailure();
    }
    return status;
}

void RadioHalHidl::Tuner::onCallback(radio_hal_event_t *halEvent)
{
    if (mCallback != 0) {
        mCallback->onEvent(halEvent);
    }
}

} // namespace android
+108 −0

File added.

Preview size limit exceeded, changes collapsed.