1/*
2 * Copyright (C) 2017 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16#define LOG_TAG "BroadcastRadioDefault.module"
17#define LOG_NDEBUG 0
18
19#include "BroadcastRadio.h"
20
21#include <log/log.h>
22
23#include "resources.h"
24
25namespace android {
26namespace hardware {
27namespace broadcastradio {
28namespace V1_1 {
29namespace implementation {
30
31using V1_0::Band;
32using V1_0::BandConfig;
33using V1_0::Class;
34using V1_0::Deemphasis;
35using V1_0::Rds;
36using V1_1::IdentifierType;
37using V1_1::ProgramSelector;
38using V1_1::ProgramType;
39using V1_1::Properties;
40using V1_1::VendorKeyValue;
41
42using std::lock_guard;
43using std::map;
44using std::mutex;
45using std::vector;
46
47// clang-format off
48static const map<Class, ModuleConfig> gModuleConfigs{
49    {Class::AM_FM, ModuleConfig({
50        "Digital radio mock",
51        {  // amFmBands
52            AmFmBandConfig({
53                Band::AM,
54                153,         // lowerLimit
55                26100,       // upperLimit
56                {5, 9, 10},  // spacings
57            }),
58            AmFmBandConfig({
59                Band::FM,
60                65800,           // lowerLimit
61                108000,          // upperLimit
62                {10, 100, 200},  // spacings
63            }),
64            AmFmBandConfig({
65                Band::AM_HD,
66                153,         // lowerLimit
67                26100,       // upperLimit
68                {5, 9, 10},  // spacings
69            }),
70            AmFmBandConfig({
71                Band::FM_HD,
72                87700,   // lowerLimit
73                107900,  // upperLimit
74                {200},   // spacings
75            }),
76        },
77    })},
78
79    {Class::SAT, ModuleConfig({
80        "Satellite radio mock",
81        {},  // amFmBands
82    })},
83};
84// clang-format on
85
86BroadcastRadio::BroadcastRadio(Class classId)
87    : mClassId(classId), mConfig(gModuleConfigs.at(classId)) {}
88
89bool BroadcastRadio::isSupported(Class classId) {
90    return gModuleConfigs.find(classId) != gModuleConfigs.end();
91}
92
93Return<void> BroadcastRadio::getProperties(getProperties_cb _hidl_cb) {
94    ALOGV("%s", __func__);
95    return getProperties_1_1(
96        [&](const Properties& properties) { _hidl_cb(Result::OK, properties.base); });
97}
98
99Return<void> BroadcastRadio::getProperties_1_1(getProperties_1_1_cb _hidl_cb) {
100    ALOGV("%s", __func__);
101    Properties prop11 = {};
102    auto& prop10 = prop11.base;
103
104    prop10.classId = mClassId;
105    prop10.implementor = "Google";
106    prop10.product = mConfig.productName;
107    prop10.numTuners = 1;
108    prop10.numAudioSources = 1;
109    prop10.supportsCapture = false;
110    prop11.supportsBackgroundScanning = true;
111    prop11.supportedProgramTypes = hidl_vec<uint32_t>({
112        static_cast<uint32_t>(ProgramType::AM), static_cast<uint32_t>(ProgramType::FM),
113        static_cast<uint32_t>(ProgramType::AM_HD), static_cast<uint32_t>(ProgramType::FM_HD),
114    });
115    prop11.supportedIdentifierTypes = hidl_vec<uint32_t>({
116        static_cast<uint32_t>(IdentifierType::AMFM_FREQUENCY),
117        static_cast<uint32_t>(IdentifierType::RDS_PI),
118        static_cast<uint32_t>(IdentifierType::HD_STATION_ID_EXT),
119        static_cast<uint32_t>(IdentifierType::HD_SUBCHANNEL),
120    });
121    prop11.vendorInfo = hidl_vec<VendorKeyValue>({
122        {"com.google.dummy", "dummy"},
123    });
124
125    prop10.bands = getAmFmBands();
126
127    _hidl_cb(prop11);
128    return Void();
129}
130
131Return<void> BroadcastRadio::openTuner(const BandConfig& config, bool audio __unused,
132                                       const sp<V1_0::ITunerCallback>& callback,
133                                       openTuner_cb _hidl_cb) {
134    ALOGV("%s(%s)", __func__, toString(config.type).c_str());
135    lock_guard<mutex> lk(mMut);
136
137    auto oldTuner = mTuner.promote();
138    if (oldTuner != nullptr) {
139        ALOGI("Force-closing previously opened tuner");
140        oldTuner->forceClose();
141        mTuner = nullptr;
142    }
143
144    sp<Tuner> newTuner = new Tuner(this, mClassId, callback);
145    mTuner = newTuner;
146    if (mClassId == Class::AM_FM) {
147        auto ret = newTuner->setConfiguration(config);
148        if (ret != Result::OK) {
149            _hidl_cb(Result::INVALID_ARGUMENTS, {});
150            return Void();
151        }
152    }
153
154    _hidl_cb(Result::OK, newTuner);
155    return Void();
156}
157
158Return<void> BroadcastRadio::getImage(int32_t id, getImage_cb _hidl_cb) {
159    ALOGV("%s(%x)", __func__, id);
160
161    if (id == resources::demoPngId) {
162        _hidl_cb(std::vector<uint8_t>(resources::demoPng, std::end(resources::demoPng)));
163        return {};
164    }
165
166    ALOGI("Image %x doesn't exists", id);
167    _hidl_cb({});
168    return Void();
169}
170
171std::vector<V1_0::BandConfig> BroadcastRadio::getAmFmBands() const {
172    std::vector<V1_0::BandConfig> out;
173    for (auto&& src : mConfig.amFmBands) {
174        V1_0::BandConfig dst;
175
176        dst.type = src.type;
177        dst.antennaConnected = true;
178        dst.lowerLimit = src.lowerLimit;
179        dst.upperLimit = src.upperLimit;
180        dst.spacings = src.spacings;
181
182        if (utils::isAm(src.type)) {
183            dst.ext.am.stereo = true;
184        } else if (utils::isFm(src.type)) {
185            dst.ext.fm.deemphasis = static_cast<Deemphasis>(Deemphasis::D50 | Deemphasis::D75);
186            dst.ext.fm.stereo = true;
187            dst.ext.fm.rds = static_cast<Rds>(Rds::WORLD | Rds::US);
188            dst.ext.fm.ta = true;
189            dst.ext.fm.af = true;
190            dst.ext.fm.ea = true;
191        }
192
193        out.push_back(dst);
194    }
195    return out;
196}
197
198}  // namespace implementation
199}  // namespace V1_1
200}  // namespace broadcastradio
201}  // namespace hardware
202}  // namespace android
203