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