blob: a28465161cc210bac7c9c5efd81913490bb2f1ca [file] [log] [blame]
Weilin Xub2a6ca62022-05-08 23:47:04 +00001/*
2 * Copyright (C) 2022 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
17#define LOG_TAG "BcRadioAidlDef.utils"
18
19#include "broadcastradio-utils-aidl/Utils.h"
20
21#include <android-base/logging.h>
22
23#include <math/HashCombine.h>
24
25namespace aidl::android::hardware::broadcastradio {
26
27namespace utils {
28
29namespace {
30
31using ::std::string;
32using ::std::vector;
33
34const int64_t kValueForNotFoundIdentifier = 0;
35
36bool bothHaveId(const ProgramSelector& a, const ProgramSelector& b, const IdentifierType& type) {
37 return hasId(a, type) && hasId(b, type);
38}
39
40bool haveEqualIds(const ProgramSelector& a, const ProgramSelector& b, const IdentifierType& type) {
41 if (!bothHaveId(a, b, type)) {
42 return false;
43 }
44 /* We should check all Ids of a given type (ie. other AF),
45 * but it doesn't matter for default implementation.
46 */
47 return getId(a, type) == getId(b, type);
48}
49
50int getHdSubchannel(const ProgramSelector& sel) {
51 int64_t hdSidExt = getId(sel, IdentifierType::HD_STATION_ID_EXT, /* defaultValue */ 0);
52 hdSidExt >>= 32; // Station ID number
53 return hdSidExt & 0xF; // HD Radio subchannel
54}
55
56bool maybeGetId(const ProgramSelector& sel, const IdentifierType& type, int64_t* val) {
57 // iterate through primaryId and secondaryIds
58 for (auto it = begin(sel); it != end(sel); it++) {
59 if (it->type == type) {
60 if (val != nullptr) {
61 *val = it->value;
62 }
63 return true;
64 }
65 }
66
67 return false;
68}
69
70} // namespace
71
72IdentifierIterator::IdentifierIterator(const ProgramSelector& sel) : IdentifierIterator(sel, 0) {}
73
74IdentifierIterator::IdentifierIterator(const ProgramSelector& sel, size_t pos)
75 : mSel(sel), mPos(pos) {}
76
77const IdentifierIterator IdentifierIterator::operator++(int) {
78 IdentifierIterator i = *this;
79 mPos++;
80 return i;
81}
82
83IdentifierIterator& IdentifierIterator::operator++() {
84 ++mPos;
85 return *this;
86}
87
88IdentifierIterator::refType IdentifierIterator::operator*() const {
89 if (mPos == 0) {
90 return getSelector().primaryId;
91 }
92
93 // mPos is 1-based for secondary identifiers
94 DCHECK(mPos <= getSelector().secondaryIds.size());
95 return getSelector().secondaryIds[mPos - 1];
96}
97
98bool IdentifierIterator::operator==(const IdentifierIterator& rhs) const {
99 // Check, if both iterators points at the same selector.
100 if (reinterpret_cast<intptr_t>(&getSelector()) !=
101 reinterpret_cast<intptr_t>(&rhs.getSelector())) {
102 return false;
103 }
104
105 return mPos == rhs.mPos;
106}
107
108int32_t resultToInt(Result result) {
109 return static_cast<int32_t>(result);
110}
111
112FrequencyBand getBand(int64_t freq) {
113 // keep in sync with
114 // frameworks/base/services/core/java/com/android/server/broadcastradio/aidl/Utils.java
115 if (freq < 30) return FrequencyBand::UNKNOWN;
116 if (freq < 500) return FrequencyBand::AM_LW;
117 if (freq < 1705) return FrequencyBand::AM_MW;
118 if (freq < 30000) return FrequencyBand::AM_SW;
119 if (freq < 60000) return FrequencyBand::UNKNOWN;
120 if (freq < 110000) return FrequencyBand::FM;
121 return FrequencyBand::UNKNOWN;
122}
123
124bool tunesTo(const ProgramSelector& a, const ProgramSelector& b) {
125 IdentifierType type = b.primaryId.type;
126
127 switch (type) {
128 case IdentifierType::HD_STATION_ID_EXT:
129 case IdentifierType::RDS_PI:
130 case IdentifierType::AMFM_FREQUENCY_KHZ:
131 if (haveEqualIds(a, b, IdentifierType::HD_STATION_ID_EXT)) return true;
132 if (haveEqualIds(a, b, IdentifierType::RDS_PI)) return true;
133 return getHdSubchannel(b) == 0 &&
134 haveEqualIds(a, b, IdentifierType::AMFM_FREQUENCY_KHZ);
135 case IdentifierType::DAB_SID_EXT:
136 return haveEqualIds(a, b, IdentifierType::DAB_SID_EXT);
137 case IdentifierType::DRMO_SERVICE_ID:
138 return haveEqualIds(a, b, IdentifierType::DRMO_SERVICE_ID);
139 case IdentifierType::SXM_SERVICE_ID:
140 return haveEqualIds(a, b, IdentifierType::SXM_SERVICE_ID);
141 default: // includes all vendor types
142 LOG(WARNING) << "unsupported program type: " << toString(type);
143 return false;
144 }
145}
146
147bool hasId(const ProgramSelector& sel, const IdentifierType& type) {
148 return maybeGetId(sel, type, /* val */ nullptr);
149}
150
151int64_t getId(const ProgramSelector& sel, const IdentifierType& type) {
152 int64_t val;
153
154 if (maybeGetId(sel, type, &val)) {
155 return val;
156 }
157
158 LOG(WARNING) << "identifier not found: " << toString(type);
159 return kValueForNotFoundIdentifier;
160}
161
162int64_t getId(const ProgramSelector& sel, const IdentifierType& type, int64_t defaultValue) {
163 if (!hasId(sel, type)) {
164 return defaultValue;
165 }
166 return getId(sel, type);
167}
168
169vector<int> getAllIds(const ProgramSelector& sel, const IdentifierType& type) {
170 vector<int> ret;
171
172 // iterate through primaryId and secondaryIds
173 for (auto it = begin(sel); it != end(sel); it++) {
174 if (it->type == type) {
175 ret.push_back(it->value);
176 }
177 }
178
179 return ret;
180}
181
182bool isSupported(const Properties& prop, const ProgramSelector& sel) {
183 for (auto it = prop.supportedIdentifierTypes.begin(); it != prop.supportedIdentifierTypes.end();
184 it++) {
185 if (hasId(sel, *it)) {
186 return true;
187 }
188 }
189 return false;
190}
191
192bool isValid(const ProgramIdentifier& id) {
193 int64_t val = id.value;
194 bool valid = true;
195
196 auto expect = [&valid](bool condition, const string& message) {
197 if (!condition) {
198 valid = false;
199 LOG(ERROR) << "identifier not valid, expected " << message;
200 }
201 };
202
203 switch (id.type) {
204 case IdentifierType::INVALID:
205 expect(false, "IdentifierType::INVALID");
206 break;
207 case IdentifierType::DAB_FREQUENCY_KHZ:
208 expect(val > 100000u, "f > 100MHz");
209 [[fallthrough]];
210 case IdentifierType::AMFM_FREQUENCY_KHZ:
211 case IdentifierType::DRMO_FREQUENCY_KHZ:
212 expect(val > 100u, "f > 100kHz");
213 expect(val < 10000000u, "f < 10GHz");
214 break;
215 case IdentifierType::RDS_PI:
216 expect(val != 0u, "RDS PI != 0");
217 expect(val <= 0xFFFFu, "16bit id");
218 break;
219 case IdentifierType::HD_STATION_ID_EXT: {
220 int64_t stationId = val & 0xFFFFFFFF; // 32bit
221 val >>= 32;
222 int64_t subchannel = val & 0xF; // 4bit
223 val >>= 4;
224 int64_t freq = val & 0x3FFFF; // 18bit
225 expect(stationId != 0u, "HD station id != 0");
226 expect(subchannel < 8u, "HD subch < 8");
227 expect(freq > 100u, "f > 100kHz");
228 expect(freq < 10000000u, "f < 10GHz");
229 break;
230 }
231 case IdentifierType::HD_STATION_NAME: {
232 while (val > 0) {
233 char ch = static_cast<char>(val & 0xFF);
234 val >>= 8;
235 expect((ch >= '0' && ch <= '9') || (ch >= 'A' && ch <= 'Z'),
236 "HD_STATION_NAME does not match [A-Z0-9]+");
237 }
238 break;
239 }
240 case IdentifierType::DAB_SID_EXT: {
241 int64_t sid = val & 0xFFFF; // 16bit
242 val >>= 16;
243 int64_t ecc = val & 0xFF; // 8bit
244 expect(sid != 0u, "DAB SId != 0");
245 expect(ecc >= 0xA0u && ecc <= 0xF6u, "Invalid ECC, see ETSI TS 101 756 V2.1.1");
246 break;
247 }
248 case IdentifierType::DAB_ENSEMBLE:
249 expect(val != 0u, "DAB ensemble != 0");
250 expect(val <= 0xFFFFu, "16bit id");
251 break;
252 case IdentifierType::DAB_SCID:
253 expect(val > 0xFu, "12bit SCId (not 4bit SCIdS)");
254 expect(val <= 0xFFFu, "12bit id");
255 break;
256 case IdentifierType::DRMO_SERVICE_ID:
257 expect(val != 0u, "DRM SId != 0");
258 expect(val <= 0xFFFFFFu, "24bit id");
259 break;
260 case IdentifierType::SXM_SERVICE_ID:
261 expect(val != 0u, "SXM SId != 0");
262 expect(val <= 0xFFFFFFFFu, "32bit id");
263 break;
264 case IdentifierType::SXM_CHANNEL:
265 expect(val < 1000u, "SXM channel < 1000");
266 break;
267 case IdentifierType::VENDOR_START:
268 case IdentifierType::VENDOR_END:
269 // skip
270 break;
271 }
272
273 return valid;
274}
275
276bool isValid(const ProgramSelector& sel) {
277 // iterate through primaryId and secondaryIds
278 for (auto it = begin(sel); it != end(sel); it++) {
279 if (!isValid(*it)) {
280 return false;
281 }
282 }
283 return true;
284}
285
286ProgramIdentifier makeIdentifier(IdentifierType type, int64_t value) {
287 return {type, value};
288}
289
290ProgramSelector makeSelectorAmfm(int32_t frequency) {
291 ProgramSelector sel = {};
292 sel.primaryId = makeIdentifier(IdentifierType::AMFM_FREQUENCY_KHZ, frequency);
293 return sel;
294}
295
296ProgramSelector makeSelectorDab(int32_t sidExt, int32_t ensemble) {
297 ProgramSelector sel = {};
298 // TODO(243686545): Have a helper function to create the sidExt instead of
299 // passing the whole identifier here. Something like makeDabSidExt.
300 sel.primaryId = makeIdentifier(IdentifierType::DAB_SID_EXT, sidExt);
301 vector<ProgramIdentifier> secondaryIds = {
302 makeIdentifier(IdentifierType::DAB_ENSEMBLE, ensemble),
303 // TODO(243686545): Include frequency here when the helper method to
304 // translate between ensemble and frequency is implemented.
305 };
306 sel.secondaryIds = std::move(secondaryIds);
307 return sel;
308}
309
310bool satisfies(const ProgramFilter& filter, const ProgramSelector& sel) {
311 if (filter.identifierTypes.size() > 0) {
312 auto typeEquals = [](const ProgramIdentifier& id, IdentifierType type) {
313 return id.type == type;
314 };
315 auto it = std::find_first_of(begin(sel), end(sel), filter.identifierTypes.begin(),
316 filter.identifierTypes.end(), typeEquals);
317 if (it == end(sel)) {
318 return false;
319 }
320 }
321
322 if (filter.identifiers.size() > 0) {
323 auto it = std::find_first_of(begin(sel), end(sel), filter.identifiers.begin(),
324 filter.identifiers.end());
325 if (it == end(sel)) {
326 return false;
327 }
328 }
329
330 if (!filter.includeCategories && sel.primaryId.type == IdentifierType::DAB_ENSEMBLE) {
331 return false;
332 }
333
334 return true;
335}
336
337size_t ProgramInfoHasher::operator()(const ProgramInfo& info) const {
338 const ProgramIdentifier& id = info.selector.primaryId;
339
340 // This is not the best hash implementation, but good enough for default HAL
341 // implementation and tests.
342 size_t h = 0;
343 ::android::hashCombineSingle(h, id.type);
344 ::android::hashCombineSingle(h, id.value);
345 return h;
346}
347
348bool ProgramInfoKeyEqual::operator()(const ProgramInfo& info1, const ProgramInfo& info2) const {
349 const ProgramIdentifier& id1 = info1.selector.primaryId;
350 const ProgramIdentifier& id2 = info2.selector.primaryId;
351 return id1.type == id2.type && id1.value == id2.value;
352}
353
354void updateProgramList(const ProgramListChunk& chunk, ProgramInfoSet* list) {
355 if (chunk.purge) {
356 list->clear();
357 }
358
359 list->insert(chunk.modified.begin(), chunk.modified.end());
360
361 if (!chunk.removed.has_value()) {
362 return;
363 }
364
365 for (auto& id : chunk.removed.value()) {
366 if (id.has_value()) {
367 ProgramInfo info = {};
368 info.selector.primaryId = id.value();
369 list->erase(info);
370 }
371 }
372}
373
374std::optional<std::string> getMetadataString(const ProgramInfo& info, const Metadata::Tag& tag) {
375 auto isRdsPs = [tag](const Metadata& item) { return item.getTag() == tag; };
376
377 auto it = std::find_if(info.metadata.begin(), info.metadata.end(), isRdsPs);
378 if (it == info.metadata.end()) {
379 return std::nullopt;
380 }
381
382 std::string metadataString;
383 switch (it->getTag()) {
384 case Metadata::rdsPs:
385 metadataString = it->get<Metadata::rdsPs>();
386 break;
387 case Metadata::rdsPty:
388 metadataString = std::to_string(it->get<Metadata::rdsPty>());
389 break;
390 case Metadata::rbdsPty:
391 metadataString = std::to_string(it->get<Metadata::rbdsPty>());
392 break;
393 case Metadata::rdsRt:
394 metadataString = it->get<Metadata::rdsRt>();
395 break;
396 case Metadata::songTitle:
397 metadataString = it->get<Metadata::songTitle>();
398 break;
399 case Metadata::songArtist:
400 metadataString = it->get<Metadata::songArtist>();
401 break;
402 case Metadata::songAlbum:
403 metadataString = it->get<Metadata::songAlbum>();
404 break;
405 case Metadata::stationIcon:
406 metadataString = std::to_string(it->get<Metadata::stationIcon>());
407 break;
408 case Metadata::albumArt:
409 metadataString = std::to_string(it->get<Metadata::albumArt>());
410 break;
411 case Metadata::programName:
412 metadataString = it->get<Metadata::programName>();
413 break;
414 case Metadata::dabEnsembleName:
415 metadataString = it->get<Metadata::dabEnsembleName>();
416 break;
417 case Metadata::dabEnsembleNameShort:
418 metadataString = it->get<Metadata::dabEnsembleNameShort>();
419 break;
420 case Metadata::dabServiceName:
421 metadataString = it->get<Metadata::dabServiceName>();
422 break;
423 case Metadata::dabServiceNameShort:
424 metadataString = it->get<Metadata::dabServiceNameShort>();
425 break;
426 case Metadata::dabComponentName:
427 metadataString = it->get<Metadata::dabComponentName>();
428 break;
429 case Metadata::dabComponentNameShort:
430 metadataString = it->get<Metadata::dabComponentNameShort>();
431 break;
432 default:
433 LOG(ERROR) << "Metadata " << it->toString() << " is not converted.";
434 return std::nullopt;
435 }
436 return metadataString;
437}
438
439ProgramIdentifier makeHdRadioStationName(const string& name) {
440 constexpr size_t maxlen = 8;
441
442 string shortName;
443 shortName.reserve(maxlen);
444
445 const auto& loc = std::locale::classic();
446 for (const char& ch : name) {
447 if (!std::isalnum(ch, loc)) {
448 continue;
449 }
450 shortName.push_back(std::toupper(ch, loc));
451 if (shortName.length() >= maxlen) {
452 break;
453 }
454 }
455
456 // Short name is converted to HD_STATION_NAME by encoding each char into its ASCII value in
457 // in little-endian order. For example, "Abc" is converted to 0x434241.
458 int64_t val = 0;
459 for (auto rit = shortName.rbegin(); rit != shortName.rend(); ++rit) {
460 val <<= 8;
461 val |= static_cast<char>(*rit);
462 }
463
464 return makeIdentifier(IdentifierType::HD_STATION_NAME, val);
465}
466
467} // namespace utils
468
469utils::IdentifierIterator begin(const ProgramSelector& sel) {
470 return utils::IdentifierIterator(sel);
471}
472
473utils::IdentifierIterator end(const ProgramSelector& sel) {
474 return utils::IdentifierIterator(sel) + 1 /* primary id */ + sel.secondaryIds.size();
475}
476
477} // namespace aidl::android::hardware::broadcastradio