blob: 28753180a6f3a3af3c1a274bf19ed84f1f4e60aa [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>
Weilin Xu97203b02022-06-23 00:19:03 +000022#include <android-base/parseint.h>
23#include <android-base/strings.h>
Weilin Xub2a6ca62022-05-08 23:47:04 +000024
25#include <math/HashCombine.h>
26
27namespace aidl::android::hardware::broadcastradio {
28
29namespace utils {
30
31namespace {
32
Weilin Xu97203b02022-06-23 00:19:03 +000033using ::android::base::EqualsIgnoreCase;
Weilin Xub2a6ca62022-05-08 23:47:04 +000034using ::std::vector;
35
36const int64_t kValueForNotFoundIdentifier = 0;
37
38bool bothHaveId(const ProgramSelector& a, const ProgramSelector& b, const IdentifierType& type) {
39 return hasId(a, type) && hasId(b, type);
40}
41
42bool haveEqualIds(const ProgramSelector& a, const ProgramSelector& b, const IdentifierType& type) {
43 if (!bothHaveId(a, b, type)) {
44 return false;
45 }
46 /* We should check all Ids of a given type (ie. other AF),
47 * but it doesn't matter for default implementation.
48 */
49 return getId(a, type) == getId(b, type);
50}
51
52int getHdSubchannel(const ProgramSelector& sel) {
53 int64_t hdSidExt = getId(sel, IdentifierType::HD_STATION_ID_EXT, /* defaultValue */ 0);
54 hdSidExt >>= 32; // Station ID number
55 return hdSidExt & 0xF; // HD Radio subchannel
56}
57
58bool maybeGetId(const ProgramSelector& sel, const IdentifierType& type, int64_t* val) {
59 // iterate through primaryId and secondaryIds
60 for (auto it = begin(sel); it != end(sel); it++) {
61 if (it->type == type) {
62 if (val != nullptr) {
63 *val = it->value;
64 }
65 return true;
66 }
67 }
68
69 return false;
70}
71
72} // namespace
73
74IdentifierIterator::IdentifierIterator(const ProgramSelector& sel) : IdentifierIterator(sel, 0) {}
75
76IdentifierIterator::IdentifierIterator(const ProgramSelector& sel, size_t pos)
77 : mSel(sel), mPos(pos) {}
78
79const IdentifierIterator IdentifierIterator::operator++(int) {
80 IdentifierIterator i = *this;
81 mPos++;
82 return i;
83}
84
85IdentifierIterator& IdentifierIterator::operator++() {
86 ++mPos;
87 return *this;
88}
89
90IdentifierIterator::refType IdentifierIterator::operator*() const {
91 if (mPos == 0) {
92 return getSelector().primaryId;
93 }
94
95 // mPos is 1-based for secondary identifiers
96 DCHECK(mPos <= getSelector().secondaryIds.size());
97 return getSelector().secondaryIds[mPos - 1];
98}
99
100bool IdentifierIterator::operator==(const IdentifierIterator& rhs) const {
101 // Check, if both iterators points at the same selector.
102 if (reinterpret_cast<intptr_t>(&getSelector()) !=
103 reinterpret_cast<intptr_t>(&rhs.getSelector())) {
104 return false;
105 }
106
107 return mPos == rhs.mPos;
108}
109
110int32_t resultToInt(Result result) {
111 return static_cast<int32_t>(result);
112}
113
114FrequencyBand getBand(int64_t freq) {
115 // keep in sync with
116 // frameworks/base/services/core/java/com/android/server/broadcastradio/aidl/Utils.java
117 if (freq < 30) return FrequencyBand::UNKNOWN;
118 if (freq < 500) return FrequencyBand::AM_LW;
119 if (freq < 1705) return FrequencyBand::AM_MW;
120 if (freq < 30000) return FrequencyBand::AM_SW;
121 if (freq < 60000) return FrequencyBand::UNKNOWN;
122 if (freq < 110000) return FrequencyBand::FM;
123 return FrequencyBand::UNKNOWN;
124}
125
126bool tunesTo(const ProgramSelector& a, const ProgramSelector& b) {
127 IdentifierType type = b.primaryId.type;
128
129 switch (type) {
130 case IdentifierType::HD_STATION_ID_EXT:
131 case IdentifierType::RDS_PI:
132 case IdentifierType::AMFM_FREQUENCY_KHZ:
133 if (haveEqualIds(a, b, IdentifierType::HD_STATION_ID_EXT)) return true;
134 if (haveEqualIds(a, b, IdentifierType::RDS_PI)) return true;
135 return getHdSubchannel(b) == 0 &&
136 haveEqualIds(a, b, IdentifierType::AMFM_FREQUENCY_KHZ);
137 case IdentifierType::DAB_SID_EXT:
Weilin Xu64cb9632023-03-15 23:46:43 +0000138 if (!haveEqualIds(a, b, IdentifierType::DAB_SID_EXT)) {
139 return false;
140 }
141 if (hasId(a, IdentifierType::DAB_ENSEMBLE) &&
142 !haveEqualIds(a, b, IdentifierType::DAB_ENSEMBLE)) {
143 return false;
144 }
145 if (hasId(a, IdentifierType::DAB_FREQUENCY_KHZ) &&
146 !haveEqualIds(a, b, IdentifierType::DAB_FREQUENCY_KHZ)) {
147 return false;
148 }
149 return true;
Weilin Xub2a6ca62022-05-08 23:47:04 +0000150 case IdentifierType::DRMO_SERVICE_ID:
151 return haveEqualIds(a, b, IdentifierType::DRMO_SERVICE_ID);
152 case IdentifierType::SXM_SERVICE_ID:
153 return haveEqualIds(a, b, IdentifierType::SXM_SERVICE_ID);
154 default: // includes all vendor types
155 LOG(WARNING) << "unsupported program type: " << toString(type);
156 return false;
157 }
158}
159
160bool hasId(const ProgramSelector& sel, const IdentifierType& type) {
161 return maybeGetId(sel, type, /* val */ nullptr);
162}
163
164int64_t getId(const ProgramSelector& sel, const IdentifierType& type) {
165 int64_t val;
166
167 if (maybeGetId(sel, type, &val)) {
168 return val;
169 }
170
171 LOG(WARNING) << "identifier not found: " << toString(type);
172 return kValueForNotFoundIdentifier;
173}
174
175int64_t getId(const ProgramSelector& sel, const IdentifierType& type, int64_t defaultValue) {
176 if (!hasId(sel, type)) {
177 return defaultValue;
178 }
179 return getId(sel, type);
180}
181
182vector<int> getAllIds(const ProgramSelector& sel, const IdentifierType& type) {
183 vector<int> ret;
184
185 // iterate through primaryId and secondaryIds
186 for (auto it = begin(sel); it != end(sel); it++) {
187 if (it->type == type) {
188 ret.push_back(it->value);
189 }
190 }
191
192 return ret;
193}
194
195bool isSupported(const Properties& prop, const ProgramSelector& sel) {
196 for (auto it = prop.supportedIdentifierTypes.begin(); it != prop.supportedIdentifierTypes.end();
197 it++) {
198 if (hasId(sel, *it)) {
199 return true;
200 }
201 }
202 return false;
203}
204
205bool isValid(const ProgramIdentifier& id) {
Weilin Xu664048f2023-09-07 11:26:38 -0700206 uint64_t val = static_cast<uint64_t>(id.value);
Weilin Xub2a6ca62022-05-08 23:47:04 +0000207 bool valid = true;
208
Weilin Xu25409e52023-09-06 10:36:24 -0700209 auto expect = [&valid](bool condition, const std::string& message) {
Weilin Xub2a6ca62022-05-08 23:47:04 +0000210 if (!condition) {
211 valid = false;
212 LOG(ERROR) << "identifier not valid, expected " << message;
213 }
214 };
215
216 switch (id.type) {
217 case IdentifierType::INVALID:
218 expect(false, "IdentifierType::INVALID");
219 break;
220 case IdentifierType::DAB_FREQUENCY_KHZ:
221 expect(val > 100000u, "f > 100MHz");
222 [[fallthrough]];
223 case IdentifierType::AMFM_FREQUENCY_KHZ:
224 case IdentifierType::DRMO_FREQUENCY_KHZ:
225 expect(val > 100u, "f > 100kHz");
226 expect(val < 10000000u, "f < 10GHz");
227 break;
228 case IdentifierType::RDS_PI:
229 expect(val != 0u, "RDS PI != 0");
230 expect(val <= 0xFFFFu, "16bit id");
231 break;
232 case IdentifierType::HD_STATION_ID_EXT: {
Weilin Xu664048f2023-09-07 11:26:38 -0700233 uint64_t stationId = val & 0xFFFFFFFF; // 32bit
Weilin Xub2a6ca62022-05-08 23:47:04 +0000234 val >>= 32;
Weilin Xu664048f2023-09-07 11:26:38 -0700235 uint64_t subchannel = val & 0xF; // 4bit
Weilin Xub2a6ca62022-05-08 23:47:04 +0000236 val >>= 4;
Weilin Xu664048f2023-09-07 11:26:38 -0700237 uint64_t freq = val & 0x3FFFF; // 18bit
Weilin Xub2a6ca62022-05-08 23:47:04 +0000238 expect(stationId != 0u, "HD station id != 0");
239 expect(subchannel < 8u, "HD subch < 8");
240 expect(freq > 100u, "f > 100kHz");
241 expect(freq < 10000000u, "f < 10GHz");
242 break;
243 }
244 case IdentifierType::HD_STATION_NAME: {
245 while (val > 0) {
246 char ch = static_cast<char>(val & 0xFF);
247 val >>= 8;
248 expect((ch >= '0' && ch <= '9') || (ch >= 'A' && ch <= 'Z'),
249 "HD_STATION_NAME does not match [A-Z0-9]+");
250 }
251 break;
252 }
253 case IdentifierType::DAB_SID_EXT: {
Weilin Xu664048f2023-09-07 11:26:38 -0700254 uint64_t sid = val & 0xFFFFFFFF; // 32bit
Weilin Xu0d4207d2022-12-09 00:37:44 +0000255 val >>= 32;
Weilin Xu664048f2023-09-07 11:26:38 -0700256 uint64_t ecc = val & 0xFF; // 8bit
Weilin Xub2a6ca62022-05-08 23:47:04 +0000257 expect(sid != 0u, "DAB SId != 0");
258 expect(ecc >= 0xA0u && ecc <= 0xF6u, "Invalid ECC, see ETSI TS 101 756 V2.1.1");
259 break;
260 }
261 case IdentifierType::DAB_ENSEMBLE:
262 expect(val != 0u, "DAB ensemble != 0");
263 expect(val <= 0xFFFFu, "16bit id");
264 break;
265 case IdentifierType::DAB_SCID:
266 expect(val > 0xFu, "12bit SCId (not 4bit SCIdS)");
267 expect(val <= 0xFFFu, "12bit id");
268 break;
269 case IdentifierType::DRMO_SERVICE_ID:
270 expect(val != 0u, "DRM SId != 0");
271 expect(val <= 0xFFFFFFu, "24bit id");
272 break;
273 case IdentifierType::SXM_SERVICE_ID:
274 expect(val != 0u, "SXM SId != 0");
275 expect(val <= 0xFFFFFFFFu, "32bit id");
276 break;
277 case IdentifierType::SXM_CHANNEL:
278 expect(val < 1000u, "SXM channel < 1000");
279 break;
Weilin Xu25409e52023-09-06 10:36:24 -0700280 default:
281 expect(id.type >= IdentifierType::VENDOR_START && id.type <= IdentifierType::VENDOR_END,
282 "Undefined identifier type");
Weilin Xub2a6ca62022-05-08 23:47:04 +0000283 break;
284 }
285
286 return valid;
287}
288
289bool isValid(const ProgramSelector& sel) {
Weilin Xu0d4207d2022-12-09 00:37:44 +0000290 if (sel.primaryId.type != IdentifierType::AMFM_FREQUENCY_KHZ &&
291 sel.primaryId.type != IdentifierType::RDS_PI &&
292 sel.primaryId.type != IdentifierType::HD_STATION_ID_EXT &&
293 sel.primaryId.type != IdentifierType::DAB_SID_EXT &&
294 sel.primaryId.type != IdentifierType::DRMO_SERVICE_ID &&
295 sel.primaryId.type != IdentifierType::SXM_SERVICE_ID &&
296 (sel.primaryId.type < IdentifierType::VENDOR_START ||
297 sel.primaryId.type > IdentifierType::VENDOR_END)) {
298 return false;
299 }
Weilin Xu64cb9632023-03-15 23:46:43 +0000300 return isValid(sel.primaryId);
Weilin Xub2a6ca62022-05-08 23:47:04 +0000301}
302
303ProgramIdentifier makeIdentifier(IdentifierType type, int64_t value) {
304 return {type, value};
305}
306
Weilin Xu664048f2023-09-07 11:26:38 -0700307ProgramSelector makeSelectorAmfm(uint32_t frequency) {
Weilin Xub2a6ca62022-05-08 23:47:04 +0000308 ProgramSelector sel = {};
309 sel.primaryId = makeIdentifier(IdentifierType::AMFM_FREQUENCY_KHZ, frequency);
310 return sel;
311}
312
Weilin Xu664048f2023-09-07 11:26:38 -0700313ProgramSelector makeSelectorDab(uint64_t sidExt) {
Weilin Xu64cb9632023-03-15 23:46:43 +0000314 ProgramSelector sel = {};
315 sel.primaryId = makeIdentifier(IdentifierType::DAB_SID_EXT, sidExt);
316 return sel;
317}
318
Weilin Xu664048f2023-09-07 11:26:38 -0700319ProgramSelector makeSelectorDab(uint64_t sidExt, uint32_t ensemble, uint64_t freq) {
Weilin Xub2a6ca62022-05-08 23:47:04 +0000320 ProgramSelector sel = {};
Weilin Xub2a6ca62022-05-08 23:47:04 +0000321 sel.primaryId = makeIdentifier(IdentifierType::DAB_SID_EXT, sidExt);
322 vector<ProgramIdentifier> secondaryIds = {
323 makeIdentifier(IdentifierType::DAB_ENSEMBLE, ensemble),
Weilin Xu0d4207d2022-12-09 00:37:44 +0000324 makeIdentifier(IdentifierType::DAB_FREQUENCY_KHZ, freq)};
Weilin Xub2a6ca62022-05-08 23:47:04 +0000325 sel.secondaryIds = std::move(secondaryIds);
326 return sel;
327}
328
329bool satisfies(const ProgramFilter& filter, const ProgramSelector& sel) {
330 if (filter.identifierTypes.size() > 0) {
331 auto typeEquals = [](const ProgramIdentifier& id, IdentifierType type) {
332 return id.type == type;
333 };
334 auto it = std::find_first_of(begin(sel), end(sel), filter.identifierTypes.begin(),
335 filter.identifierTypes.end(), typeEquals);
336 if (it == end(sel)) {
337 return false;
338 }
339 }
340
341 if (filter.identifiers.size() > 0) {
342 auto it = std::find_first_of(begin(sel), end(sel), filter.identifiers.begin(),
343 filter.identifiers.end());
344 if (it == end(sel)) {
345 return false;
346 }
347 }
348
Weilin Xub2a6ca62022-05-08 23:47:04 +0000349 return true;
350}
351
352size_t ProgramInfoHasher::operator()(const ProgramInfo& info) const {
353 const ProgramIdentifier& id = info.selector.primaryId;
354
355 // This is not the best hash implementation, but good enough for default HAL
356 // implementation and tests.
357 size_t h = 0;
358 ::android::hashCombineSingle(h, id.type);
359 ::android::hashCombineSingle(h, id.value);
360 return h;
361}
362
363bool ProgramInfoKeyEqual::operator()(const ProgramInfo& info1, const ProgramInfo& info2) const {
364 const ProgramIdentifier& id1 = info1.selector.primaryId;
365 const ProgramIdentifier& id2 = info2.selector.primaryId;
366 return id1.type == id2.type && id1.value == id2.value;
367}
368
369void updateProgramList(const ProgramListChunk& chunk, ProgramInfoSet* list) {
370 if (chunk.purge) {
371 list->clear();
372 }
373
374 list->insert(chunk.modified.begin(), chunk.modified.end());
375
376 if (!chunk.removed.has_value()) {
377 return;
378 }
379
380 for (auto& id : chunk.removed.value()) {
381 if (id.has_value()) {
382 ProgramInfo info = {};
383 info.selector.primaryId = id.value();
384 list->erase(info);
385 }
386 }
387}
388
389std::optional<std::string> getMetadataString(const ProgramInfo& info, const Metadata::Tag& tag) {
390 auto isRdsPs = [tag](const Metadata& item) { return item.getTag() == tag; };
391
392 auto it = std::find_if(info.metadata.begin(), info.metadata.end(), isRdsPs);
393 if (it == info.metadata.end()) {
394 return std::nullopt;
395 }
396
397 std::string metadataString;
398 switch (it->getTag()) {
399 case Metadata::rdsPs:
400 metadataString = it->get<Metadata::rdsPs>();
401 break;
402 case Metadata::rdsPty:
403 metadataString = std::to_string(it->get<Metadata::rdsPty>());
404 break;
405 case Metadata::rbdsPty:
406 metadataString = std::to_string(it->get<Metadata::rbdsPty>());
407 break;
408 case Metadata::rdsRt:
409 metadataString = it->get<Metadata::rdsRt>();
410 break;
411 case Metadata::songTitle:
412 metadataString = it->get<Metadata::songTitle>();
413 break;
414 case Metadata::songArtist:
415 metadataString = it->get<Metadata::songArtist>();
416 break;
417 case Metadata::songAlbum:
418 metadataString = it->get<Metadata::songAlbum>();
419 break;
420 case Metadata::stationIcon:
421 metadataString = std::to_string(it->get<Metadata::stationIcon>());
422 break;
423 case Metadata::albumArt:
424 metadataString = std::to_string(it->get<Metadata::albumArt>());
425 break;
426 case Metadata::programName:
427 metadataString = it->get<Metadata::programName>();
428 break;
429 case Metadata::dabEnsembleName:
430 metadataString = it->get<Metadata::dabEnsembleName>();
431 break;
432 case Metadata::dabEnsembleNameShort:
433 metadataString = it->get<Metadata::dabEnsembleNameShort>();
434 break;
435 case Metadata::dabServiceName:
436 metadataString = it->get<Metadata::dabServiceName>();
437 break;
438 case Metadata::dabServiceNameShort:
439 metadataString = it->get<Metadata::dabServiceNameShort>();
440 break;
441 case Metadata::dabComponentName:
442 metadataString = it->get<Metadata::dabComponentName>();
443 break;
444 case Metadata::dabComponentNameShort:
445 metadataString = it->get<Metadata::dabComponentNameShort>();
446 break;
447 default:
448 LOG(ERROR) << "Metadata " << it->toString() << " is not converted.";
449 return std::nullopt;
450 }
451 return metadataString;
452}
453
Weilin Xu25409e52023-09-06 10:36:24 -0700454ProgramIdentifier makeHdRadioStationName(const std::string& name) {
Weilin Xub2a6ca62022-05-08 23:47:04 +0000455 constexpr size_t maxlen = 8;
456
Weilin Xu25409e52023-09-06 10:36:24 -0700457 std::string shortName;
Weilin Xub2a6ca62022-05-08 23:47:04 +0000458 shortName.reserve(maxlen);
459
460 const auto& loc = std::locale::classic();
461 for (const char& ch : name) {
462 if (!std::isalnum(ch, loc)) {
463 continue;
464 }
465 shortName.push_back(std::toupper(ch, loc));
466 if (shortName.length() >= maxlen) {
467 break;
468 }
469 }
470
471 // Short name is converted to HD_STATION_NAME by encoding each char into its ASCII value in
472 // in little-endian order. For example, "Abc" is converted to 0x434241.
473 int64_t val = 0;
474 for (auto rit = shortName.rbegin(); rit != shortName.rend(); ++rit) {
475 val <<= 8;
476 val |= static_cast<char>(*rit);
477 }
478
479 return makeIdentifier(IdentifierType::HD_STATION_NAME, val);
480}
481
Weilin Xu97203b02022-06-23 00:19:03 +0000482IdentifierType getType(int typeAsInt) {
483 return static_cast<IdentifierType>(typeAsInt);
484}
485
Weilin Xu25409e52023-09-06 10:36:24 -0700486bool parseArgInt(const std::string& s, int* out) {
Weilin Xu97203b02022-06-23 00:19:03 +0000487 return ::android::base::ParseInt(s, out);
488}
489
490bool parseArgLong(const std::string& s, long* out) {
491 return ::android::base::ParseInt(s, out);
492}
493
Weilin Xu25409e52023-09-06 10:36:24 -0700494bool parseArgBool(const std::string& s, bool* out) {
Weilin Xu97203b02022-06-23 00:19:03 +0000495 if (EqualsIgnoreCase(s, "true")) {
496 *out = true;
497 } else if (EqualsIgnoreCase(s, "false")) {
498 *out = false;
499 } else {
500 return false;
501 }
502 return true;
503}
504
Weilin Xu25409e52023-09-06 10:36:24 -0700505bool parseArgDirection(const std::string& s, bool* out) {
Weilin Xu97203b02022-06-23 00:19:03 +0000506 if (EqualsIgnoreCase(s, "up")) {
507 *out = true;
508 } else if (EqualsIgnoreCase(s, "down")) {
509 *out = false;
510 } else {
511 return false;
512 }
513 return true;
514}
515
Weilin Xu25409e52023-09-06 10:36:24 -0700516bool parseArgIdentifierTypeArray(const std::string& s, vector<IdentifierType>* out) {
517 for (const std::string& val : ::android::base::Split(s, ",")) {
Weilin Xu97203b02022-06-23 00:19:03 +0000518 int outInt;
519 if (!parseArgInt(val, &outInt)) {
520 return false;
521 }
522 out->push_back(getType(outInt));
523 }
524 return true;
525}
526
527bool parseProgramIdentifierList(const std::string& s, vector<ProgramIdentifier>* out) {
Weilin Xu25409e52023-09-06 10:36:24 -0700528 for (const std::string& idStr : ::android::base::Split(s, ",")) {
529 const vector<std::string> idStrPair = ::android::base::Split(idStr, ":");
Weilin Xu97203b02022-06-23 00:19:03 +0000530 if (idStrPair.size() != 2) {
531 return false;
532 }
533 int idType;
534 if (!parseArgInt(idStrPair[0], &idType)) {
535 return false;
536 }
537 long idVal;
538 if (!parseArgLong(idStrPair[1], &idVal)) {
539 return false;
540 }
541 ProgramIdentifier id = {getType(idType), idVal};
542 out->push_back(id);
543 }
544 return true;
545}
546
Weilin Xub2a6ca62022-05-08 23:47:04 +0000547} // namespace utils
548
549utils::IdentifierIterator begin(const ProgramSelector& sel) {
550 return utils::IdentifierIterator(sel);
551}
552
553utils::IdentifierIterator end(const ProgramSelector& sel) {
554 return utils::IdentifierIterator(sel) + 1 /* primary id */ + sel.secondaryIds.size();
555}
556
557} // namespace aidl::android::hardware::broadcastradio