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
25 namespace android {
26 namespace hardware {
27 namespace broadcastradio {
28 namespace V1_1 {
29 namespace implementation {
30
31 using V1_0::Band;
32 using V1_0::BandConfig;
33 using V1_0::Class;
34 using V1_0::Deemphasis;
35 using V1_0::Rds;
36 using V1_1::IdentifierType;
37 using V1_1::ProgramSelector;
38 using V1_1::ProgramType;
39 using V1_1::Properties;
40 using V1_1::VendorKeyValue;
41
42 using std::lock_guard;
43 using std::map;
44 using std::mutex;
45 using std::vector;
46
47 // clang-format off
48 static 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
BroadcastRadio(Class classId)86 BroadcastRadio::BroadcastRadio(Class classId)
87 : mClassId(classId), mConfig(gModuleConfigs.at(classId)) {}
88
isSupported(Class classId)89 bool BroadcastRadio::isSupported(Class classId) {
90 return gModuleConfigs.find(classId) != gModuleConfigs.end();
91 }
92
getProperties(getProperties_cb _hidl_cb)93 Return<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
getProperties_1_1(getProperties_1_1_cb _hidl_cb)99 Return<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
openTuner(const BandConfig & config,bool audio __unused,const sp<V1_0::ITunerCallback> & callback,openTuner_cb _hidl_cb)131 Return<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
getImage(int32_t id,getImage_cb _hidl_cb)158 Return<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
getAmFmBands() const171 std::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