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