| /* |
| * Copyright (C) 2017 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 "BroadcastRadioDefault.module" |
| #define LOG_NDEBUG 0 |
| |
| #include "BroadcastRadio.h" |
| |
| #include <log/log.h> |
| |
| #include "resources.h" |
| |
| namespace android { |
| namespace hardware { |
| namespace broadcastradio { |
| namespace V1_1 { |
| namespace implementation { |
| |
| using V1_0::Band; |
| using V1_0::BandConfig; |
| using V1_0::Class; |
| using V1_0::Deemphasis; |
| using V1_0::Rds; |
| using V1_1::IdentifierType; |
| using V1_1::ProgramSelector; |
| using V1_1::ProgramType; |
| using V1_1::Properties; |
| using V1_1::VendorKeyValue; |
| |
| 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, |
| 153, // lowerLimit |
| 26100, // upperLimit |
| {5, 9, 10}, // spacings |
| }), |
| AmFmBandConfig({ |
| Band::FM, |
| 65800, // lowerLimit |
| 108000, // upperLimit |
| {10, 100, 200}, // spacings |
| }), |
| AmFmBandConfig({ |
| Band::AM_HD, |
| 153, // lowerLimit |
| 26100, // upperLimit |
| {5, 9, 10}, // spacings |
| }), |
| AmFmBandConfig({ |
| Band::FM_HD, |
| 87700, // lowerLimit |
| 107900, // upperLimit |
| {200}, // spacings |
| }), |
| }, |
| })}, |
| |
| {Class::SAT, ModuleConfig({ |
| "Satellite radio mock", |
| {}, // amFmBands |
| })}, |
| }; |
| // clang-format on |
| |
| BroadcastRadio::BroadcastRadio(Class classId) |
| : mClassId(classId), mConfig(gModuleConfigs.at(classId)) {} |
| |
| bool BroadcastRadio::isSupported(Class classId) { |
| return gModuleConfigs.find(classId) != gModuleConfigs.end(); |
| } |
| |
| Return<void> BroadcastRadio::getProperties(getProperties_cb _hidl_cb) { |
| ALOGV("%s", __func__); |
| return getProperties_1_1( |
| [&](const Properties& properties) { _hidl_cb(Result::OK, properties.base); }); |
| } |
| |
| 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 = true; |
| prop11.supportedProgramTypes = hidl_vec<uint32_t>({ |
| static_cast<uint32_t>(ProgramType::AM), static_cast<uint32_t>(ProgramType::FM), |
| static_cast<uint32_t>(ProgramType::AM_HD), static_cast<uint32_t>(ProgramType::FM_HD), |
| }); |
| prop11.supportedIdentifierTypes = hidl_vec<uint32_t>({ |
| static_cast<uint32_t>(IdentifierType::AMFM_FREQUENCY), |
| static_cast<uint32_t>(IdentifierType::RDS_PI), |
| static_cast<uint32_t>(IdentifierType::HD_STATION_ID_EXT), |
| static_cast<uint32_t>(IdentifierType::HD_SUBCHANNEL), |
| }); |
| prop11.vendorInfo = hidl_vec<VendorKeyValue>({ |
| {"com.google.dummy", "dummy"}, |
| }); |
| |
| prop10.bands = getAmFmBands(); |
| |
| _hidl_cb(prop11); |
| return Void(); |
| } |
| |
| Return<void> BroadcastRadio::openTuner(const BandConfig& config, bool audio __unused, |
| const sp<V1_0::ITunerCallback>& callback, |
| openTuner_cb _hidl_cb) { |
| ALOGV("%s(%s)", __func__, toString(config.type).c_str()); |
| lock_guard<mutex> lk(mMut); |
| |
| auto oldTuner = mTuner.promote(); |
| if (oldTuner != nullptr) { |
| ALOGI("Force-closing previously opened tuner"); |
| oldTuner->forceClose(); |
| mTuner = nullptr; |
| } |
| |
| sp<Tuner> newTuner = new Tuner(this, mClassId, callback); |
| mTuner = newTuner; |
| if (mClassId == Class::AM_FM) { |
| auto ret = newTuner->setConfiguration(config); |
| if (ret != Result::OK) { |
| _hidl_cb(Result::INVALID_ARGUMENTS, {}); |
| return Void(); |
| } |
| } |
| |
| _hidl_cb(Result::OK, newTuner); |
| return Void(); |
| } |
| |
| Return<void> BroadcastRadio::getImage(int32_t id, getImage_cb _hidl_cb) { |
| ALOGV("%s(%x)", __func__, id); |
| |
| if (id == resources::demoPngId) { |
| _hidl_cb(std::vector<uint8_t>(resources::demoPng, std::end(resources::demoPng))); |
| return {}; |
| } |
| |
| ALOGI("Image %x doesn't exists", id); |
| _hidl_cb({}); |
| return Void(); |
| } |
| |
| std::vector<V1_0::BandConfig> BroadcastRadio::getAmFmBands() const { |
| std::vector<V1_0::BandConfig> out; |
| for (auto&& src : mConfig.amFmBands) { |
| V1_0::BandConfig dst; |
| |
| dst.type = src.type; |
| dst.antennaConnected = true; |
| dst.lowerLimit = src.lowerLimit; |
| dst.upperLimit = src.upperLimit; |
| dst.spacings = src.spacings; |
| |
| if (utils::isAm(src.type)) { |
| dst.ext.am.stereo = true; |
| } else if (utils::isFm(src.type)) { |
| dst.ext.fm.deemphasis = static_cast<Deemphasis>(Deemphasis::D50 | Deemphasis::D75); |
| dst.ext.fm.stereo = true; |
| dst.ext.fm.rds = static_cast<Rds>(Rds::WORLD | Rds::US); |
| dst.ext.fm.ta = true; |
| dst.ext.fm.af = true; |
| dst.ext.fm.ea = true; |
| } |
| |
| out.push_back(dst); |
| } |
| return out; |
| } |
| |
| } // namespace implementation |
| } // namespace V1_1 |
| } // namespace broadcastradio |
| } // namespace hardware |
| } // namespace android |