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

Commit 4837755c authored by Tomasz Wasilczyk's avatar Tomasz Wasilczyk
Browse files

Initial Broadcast Radio HAL 1.1 default implementation.

The new implementation does not rely on legacy HAL, it implements
HAL 1.1 directly instead.

This is not a full implementation yet, but passes all existing tests.

Bug: b/36864090
Test: VTS, instrumentalization, Unit
Change-Id: Ic8eddd19d0bf6777b4086d099183d71e05d7da0e
parent 0b11bd52
Loading
Loading
Loading
Loading
+5 −3
Original line number Diff line number Diff line
@@ -27,16 +27,18 @@ cc_library_shared {
        "BroadcastRadio.cpp",
        "BroadcastRadioFactory.cpp",
        "Tuner.cpp",
        "Utils.cpp",
        "VirtualProgram.cpp",
        "VirtualRadio.cpp",
    ],
    static_libs: [
        "android.hardware.broadcastradio@1.1-utils-lib",
    ],
    shared_libs: [
        "libhidlbase",
        "libhidltransport",
        "libutils",
        "liblog",
        "libhardware",
        "android.hardware.broadcastradio@1.0",
        "android.hardware.broadcastradio@1.1",
        "libradio_metadata",
    ],
}
+106 −107
Original line number Diff line number Diff line
@@ -13,14 +13,12 @@
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
#define LOG_TAG "BroadcastRadio"
//#define LOG_NDEBUG 0

#include <log/log.h>
#define LOG_TAG "BroadcastRadioDefault.module"
#define LOG_NDEBUG 0

#include "BroadcastRadio.h"
#include "Tuner.h"
#include "Utils.h"

#include <log/log.h>

namespace android {
namespace hardware {
@@ -28,121 +26,122 @@ namespace broadcastradio {
namespace V1_1 {
namespace implementation {

using ::android::sp;
using V1_0::Band;
using V1_0::BandConfig;
using V1_0::Class;
using V1_0::Deemphasis;
using V1_0::Rds;

using std::lock_guard;
using std::map;
using std::mutex;
using std::vector;

// clang-format off
static const map<Class, ModuleConfig> gModuleConfigs{
    {Class::AM_FM, ModuleConfig({
        "Digital radio mock",
        {  // amFmBands
            AmFmBandConfig({
                Band::AM_HD,
                540,   // lowerLimit
                1610,  // upperLimit
                10,    // spacing
            }),
            AmFmBandConfig({
                Band::FM_HD,
                87900,   // lowerLimit
                107900,  // upperLimit
                200,     // spacing
            }),
        },
    })},

    {Class::SAT, ModuleConfig({
        "Satellite radio mock",
        {},  // amFmBands
    })},
};
// clang-format on

BroadcastRadio::BroadcastRadio(Class classId)
        : mStatus(Result::NOT_INITIALIZED), mClassId(classId), mHwDevice(NULL)
{
}
    : mClassId(classId), mConfig(gModuleConfigs.at(classId)) {}

BroadcastRadio::~BroadcastRadio()
{
    if (mHwDevice != NULL) {
        radio_hw_device_close(mHwDevice);
    }
bool BroadcastRadio::isSupported(Class classId) {
    return gModuleConfigs.find(classId) != gModuleConfigs.end();
}

void BroadcastRadio::onFirstRef()
{
    const hw_module_t *mod;
    int rc;
    ALOGI("%s mClassId %d", __FUNCTION__, mClassId);

    mHwDevice = NULL;
    const char *classString = Utils::getClassString(mClassId);
    if (classString == NULL) {
        ALOGE("invalid class ID %d", mClassId);
        mStatus = Result::INVALID_ARGUMENTS;
        return;
Return<void> BroadcastRadio::getProperties(getProperties_cb _hidl_cb) {
    ALOGV("%s", __func__);
    return getProperties_1_1(
        [&](const Properties& properties) { _hidl_cb(Result::OK, properties.base); });
}

    ALOGI("%s RADIO_HARDWARE_MODULE_ID %s %s",
            __FUNCTION__, RADIO_HARDWARE_MODULE_ID, classString);

    rc = hw_get_module_by_class(RADIO_HARDWARE_MODULE_ID, classString, &mod);
    if (rc != 0) {
        ALOGE("couldn't load radio module %s.%s (%s)",
                RADIO_HARDWARE_MODULE_ID, classString, strerror(-rc));
        return;
    }
    rc = radio_hw_device_open(mod, &mHwDevice);
    if (rc != 0) {
        ALOGE("couldn't open radio hw device in %s.%s (%s)",
                RADIO_HARDWARE_MODULE_ID, "primary", strerror(-rc));
        mHwDevice = NULL;
        return;
    }
    if (mHwDevice->common.version != RADIO_DEVICE_API_VERSION_CURRENT) {
        ALOGE("wrong radio hw device version %04x", mHwDevice->common.version);
        radio_hw_device_close(mHwDevice);
        mHwDevice = NULL;
    } else {
        mStatus = Result::OK;
    }
}

int BroadcastRadio::closeHalTuner(const struct radio_tuner *halTuner)
{
    ALOGV("%s", __FUNCTION__);
    if (mHwDevice == NULL) {
        return -ENODEV;
    }
    if (halTuner == 0) {
        return -EINVAL;
    }
    return mHwDevice->close_tuner(mHwDevice, halTuner);
}


// Methods from ::android::hardware::broadcastradio::V1_1::IBroadcastRadio follow.
Return<void> BroadcastRadio::getProperties(getProperties_cb _hidl_cb)
{
    int rc;
    radio_hal_properties_t halProperties;
    Properties properties;

    if (mHwDevice == NULL) {
        rc = -ENODEV;
        goto exit;
    }
    rc = mHwDevice->get_properties(mHwDevice, &halProperties);
    if (rc == 0) {
        Utils::convertPropertiesFromHal(&properties, &halProperties);
    }

exit:
    _hidl_cb(Utils::convertHalResult(rc), properties);
Return<void> BroadcastRadio::getProperties_1_1(getProperties_1_1_cb _hidl_cb) {
    ALOGV("%s", __func__);
    Properties prop11 = {};
    auto& prop10 = prop11.base;

    prop10.classId = mClassId;
    prop10.implementor = "Google";
    prop10.product = mConfig.productName;
    prop10.numTuners = 1;
    prop10.numAudioSources = 1;
    prop10.supportsCapture = false;
    prop11.supportsBackgroundScanning = false;
    prop11.vendorExension = "dummy";

    prop10.bands.resize(mConfig.amFmBands.size());
    for (size_t i = 0; i < mConfig.amFmBands.size(); i++) {
        auto& src = mConfig.amFmBands[i];
        auto& dst = prop10.bands[i];

        dst.type = src.type;
        dst.antennaConnected = true;
        dst.lowerLimit = src.lowerLimit;
        dst.upperLimit = src.upperLimit;
        dst.spacings = vector<uint32_t>({src.spacing});

        if (src.type == Band::AM) {
            dst.ext.am.stereo = true;
        } else if (src.type == Band::FM) {
            dst.ext.fm.deemphasis = Deemphasis::D75;
            dst.ext.fm.stereo = true;
            dst.ext.fm.rds = Rds::US;
            dst.ext.fm.ta = true;
            dst.ext.fm.af = true;
            dst.ext.fm.ea = true;
        }
    }

    _hidl_cb(prop11);
    return Void();
}

Return<void> BroadcastRadio::getProperties_1_1(getProperties_1_1_cb _hidl_cb) {
    radio_hal_properties_t halProperties;
    V1_1::Properties properties = {};
Return<void> BroadcastRadio::openTuner(const BandConfig& config, bool audio __unused,
                                       const sp<V1_0::ITunerCallback>& callback,
                                       openTuner_cb _hidl_cb) {
    ALOGV("%s", __func__);
    lock_guard<mutex> lk(mMut);

    LOG_ALWAYS_FATAL_IF(mHwDevice == nullptr, "HW device is not set");
    int rc = mHwDevice->get_properties(mHwDevice, &halProperties);
    LOG_ALWAYS_FATAL_IF(rc != 0, "Couldn't get device properties");
    Utils::convertPropertiesFromHal(&properties.base, &halProperties);
    auto oldTuner = mTuner.promote();
    if (oldTuner != nullptr) {
        ALOGI("Force-closing previously opened tuner");
        oldTuner->forceClose();
        mTuner = nullptr;
    }

    _hidl_cb(properties);
    sp<Tuner> newTuner = new Tuner(callback);
    mTuner = newTuner;
    if (mClassId == Class::AM_FM) {
        auto ret = newTuner->setConfiguration(config);
        if (ret != Result::OK) {
            _hidl_cb(Result::INVALID_ARGUMENTS, {});
            return Void();
        }

Return<void> BroadcastRadio::openTuner(const BandConfig& config, bool audio,
    const sp<V1_0::ITunerCallback>& callback, openTuner_cb _hidl_cb)
{
    sp<Tuner> 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);
    _hidl_cb(Result::OK, newTuner);
    return Void();
}

+37 −30
Original line number Diff line number Diff line
@@ -16,9 +16,10 @@
#ifndef ANDROID_HARDWARE_BROADCASTRADIO_V1_1_BROADCASTRADIO_H
#define ANDROID_HARDWARE_BROADCASTRADIO_V1_1_BROADCASTRADIO_H

#include "Tuner.h"

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

namespace android {
namespace hardware {
@@ -26,42 +27,48 @@ namespace broadcastradio {
namespace V1_1 {
namespace implementation {

using V1_0::Class;
using V1_0::BandConfig;
using V1_0::Properties;
struct AmFmBandConfig {
    V1_0::Band type;
    uint32_t lowerLimit;  // kHz
    uint32_t upperLimit;  // kHz
    uint32_t spacing;     // kHz
};

struct ModuleConfig {
    std::string productName;
    std::vector<AmFmBandConfig> amFmBands;
};

struct BroadcastRadio : public V1_1::IBroadcastRadio {
    /**
     * Constructs new broadcast radio module.
     *
     * Before calling a constructor with a given classId, it must be checked with isSupported
     * method first. Otherwise it results in undefined behaviour.
     *
     * @param classId type of a radio.
     */
    BroadcastRadio(V1_0::Class classId);

    BroadcastRadio(Class classId);
    /**
     * Checks, if a given radio type is supported.
     *
     * @param classId type of a radio.
     */
    static bool isSupported(V1_0::Class classId);

    // Methods from ::android::hardware::broadcastradio::V1_1::IBroadcastRadio follow.
    // V1_1::IBroadcastRadio methods
    Return<void> getProperties(getProperties_cb _hidl_cb) override;
    Return<void> getProperties_1_1(getProperties_1_1_cb _hidl_cb) override;
    Return<void> openTuner(const BandConfig& config, bool audio,
            const sp<V1_0::ITunerCallback>& callback, openTuner_cb _hidl_cb) override;

    // RefBase
    virtual void onFirstRef() override;

    Result initCheck() { return mStatus; }
    int closeHalTuner(const struct radio_tuner *halTuner);
    Return<void> openTuner(const V1_0::BandConfig& config, bool audio,
                           const sp<V1_0::ITunerCallback>& callback,
                           openTuner_cb _hidl_cb) override;

   private:
    virtual ~BroadcastRadio();

    static const char * sClassModuleNames[];

    Result convertHalResult(int rc);
    void convertBandConfigFromHal(BandConfig *config,
            const radio_hal_band_config_t *halConfig);
    void convertPropertiesFromHal(Properties *properties,
            const radio_hal_properties_t *halProperties);
    void convertBandConfigToHal(radio_hal_band_config_t *halConfig,
            const BandConfig *config);

    Result mStatus;
    Class mClassId;
    struct radio_hw_device *mHwDevice;
    std::mutex mMut;
    V1_0::Class mClassId;
    ModuleConfig mConfig;
    wp<Tuner> mTuner;
};

}  // namespace implementation
+32 −10
Original line number Diff line number Diff line
@@ -13,29 +13,51 @@
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
#define LOG_TAG "BroadcastRadioDefault.factory"
#define LOG_NDEBUG 0

#include "BroadcastRadioFactory.h"

#include "BroadcastRadio.h"

#include <log/log.h>

namespace android {
namespace hardware {
namespace broadcastradio {
namespace V1_1 {
namespace implementation {

// Methods from ::android::hardware::broadcastradio::V1_0::IBroadcastRadioFactory follow.
Return<void> BroadcastRadioFactory::connectModule(Class classId, connectModule_cb _hidl_cb)  {
    sp<BroadcastRadio> impl = new BroadcastRadio(classId);
    Result retval = Result::NOT_INITIALIZED;
    if (impl != 0) {
        retval = impl->initCheck();
using V1_0::Class;

using std::vector;

static const vector<Class> gAllClasses = {
    Class::AM_FM, Class::SAT, Class::DT,
};

IBroadcastRadioFactory* HIDL_FETCH_IBroadcastRadioFactory(const char* name __unused) {
    return new BroadcastRadioFactory();
}
    _hidl_cb(retval, impl);
    return Void();

BroadcastRadioFactory::BroadcastRadioFactory() {
    for (auto&& classId : gAllClasses) {
        if (!BroadcastRadio::isSupported(classId)) continue;
        mRadioModules[classId] = new BroadcastRadio(classId);
    }
}

Return<void> BroadcastRadioFactory::connectModule(Class classId, connectModule_cb _hidl_cb) {
    ALOGV("%s", __func__);

    auto moduleIt = mRadioModules.find(classId);
    if (moduleIt == mRadioModules.end()) {
        _hidl_cb(Result::INVALID_ARGUMENTS, nullptr);
    } else {
        _hidl_cb(Result::OK, moduleIt->second);
    }

IBroadcastRadioFactory* HIDL_FETCH_IBroadcastRadioFactory(const char* /* name */) {
    return new BroadcastRadioFactory();
    return Void();
}

}  // namespace implementation
+9 −5
Original line number Diff line number Diff line
@@ -16,6 +16,7 @@
#ifndef ANDROID_HARDWARE_BROADCASTRADIO_V1_1_BROADCASTRADIOFACTORY_H
#define ANDROID_HARDWARE_BROADCASTRADIO_V1_1_BROADCASTRADIOFACTORY_H

#include <android/hardware/broadcastradio/1.1/IBroadcastRadio.h>
#include <android/hardware/broadcastradio/1.1/IBroadcastRadioFactory.h>
#include <android/hardware/broadcastradio/1.1/types.h>

@@ -25,14 +26,17 @@ namespace broadcastradio {
namespace V1_1 {
namespace implementation {

using V1_0::Class;
extern "C" IBroadcastRadioFactory* HIDL_FETCH_IBroadcastRadioFactory(const char* name);

struct BroadcastRadioFactory : public IBroadcastRadioFactory {
    // Methods from ::android::hardware::broadcastradio::V1_0::IBroadcastRadioFactory follow.
    Return<void> connectModule(Class classId, connectModule_cb _hidl_cb) override;
};
    BroadcastRadioFactory();

extern "C" IBroadcastRadioFactory* HIDL_FETCH_IBroadcastRadioFactory(const char* name);
    // V1_0::IBroadcastRadioFactory methods
    Return<void> connectModule(V1_0::Class classId, connectModule_cb _hidl_cb) override;

   private:
    std::map<V1_0::Class, sp<IBroadcastRadio>> mRadioModules;
};

}  // namespace implementation
}  // namespace V1_1
Loading