blob: 6fe95549d8bb92cbb1c9865e76221d80bce4c686 [file] [log] [blame]
Tomasz Wasilczyk06100b32017-12-04 09:53:32 -08001/*
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 "BcRadioDef.utils"
17//#define LOG_NDEBUG 0
18
19#include <broadcastradio-utils-2x/Utils.h>
20
Tomasz Wasilczykbceb8852017-12-18 13:59:29 -080021#include <android-base/logging.h>
Tomasz Wasilczyk06100b32017-12-04 09:53:32 -080022#include <log/log.h>
23
24namespace android {
25namespace hardware {
26namespace broadcastradio {
27namespace utils {
28
29using V2_0::IdentifierType;
30using V2_0::Metadata;
31using V2_0::MetadataKey;
Tomasz Wasilczykbceb8852017-12-18 13:59:29 -080032using V2_0::ProgramFilter;
Tomasz Wasilczyk06100b32017-12-04 09:53:32 -080033using V2_0::ProgramIdentifier;
Tomasz Wasilczykbceb8852017-12-18 13:59:29 -080034using V2_0::ProgramInfo;
35using V2_0::ProgramListChunk;
Tomasz Wasilczyk06100b32017-12-04 09:53:32 -080036using V2_0::ProgramSelector;
Tomasz Wasilczykbceb8852017-12-18 13:59:29 -080037using V2_0::Properties;
Tomasz Wasilczyk06100b32017-12-04 09:53:32 -080038
39using std::string;
Tomasz Wasilczyk31e86322017-12-05 09:36:11 -080040using std::vector;
Tomasz Wasilczyk06100b32017-12-04 09:53:32 -080041
Tomasz Wasilczykbceb8852017-12-18 13:59:29 -080042IdentifierType getType(uint32_t typeAsInt) {
43 return static_cast<IdentifierType>(typeAsInt);
44}
45
Tomasz Wasilczyk06100b32017-12-04 09:53:32 -080046IdentifierType getType(const ProgramIdentifier& id) {
Tomasz Wasilczykbceb8852017-12-18 13:59:29 -080047 return getType(id.type);
48}
49
50IdentifierIterator::IdentifierIterator(const V2_0::ProgramSelector& sel)
51 : IdentifierIterator(sel, 0) {}
52
53IdentifierIterator::IdentifierIterator(const V2_0::ProgramSelector& sel, size_t pos)
54 : mSel(sel), mPos(pos) {}
55
56IdentifierIterator IdentifierIterator::operator++(int) {
57 auto i = *this;
58 mPos++;
59 return i;
60}
61
62IdentifierIterator& IdentifierIterator::operator++() {
63 ++mPos;
64 return *this;
65}
66
67IdentifierIterator::ref_type IdentifierIterator::operator*() const {
68 if (mPos == 0) return sel().primaryId;
69
70 // mPos is 1-based for secondary identifiers
71 DCHECK(mPos <= sel().secondaryIds.size());
72 return sel().secondaryIds[mPos - 1];
73}
74
75bool IdentifierIterator::operator==(const IdentifierIterator& rhs) const {
76 // Check, if both iterators points at the same selector.
77 if (reinterpret_cast<uintptr_t>(&sel()) != reinterpret_cast<uintptr_t>(&rhs.sel())) {
78 return false;
79 }
80
81 return mPos == rhs.mPos;
82}
83
84IdentifierIterator begin(const V2_0::ProgramSelector& sel) {
85 return IdentifierIterator(sel);
86}
87
88IdentifierIterator end(const V2_0::ProgramSelector& sel) {
89 return IdentifierIterator(sel) + 1 /* primary id */ + sel.secondaryIds.size();
Tomasz Wasilczyk06100b32017-12-04 09:53:32 -080090}
91
Tomasz Wasilczyk8b70ee42017-12-21 11:51:29 -080092FrequencyBand getBand(uint64_t freq) {
93 // keep in sync with
94 // frameworks/base/services/core/java/com/android/server/broadcastradio/hal2/Utils.java
95 if (freq < 30) return FrequencyBand::UNKNOWN;
96 if (freq < 500) return FrequencyBand::AM_LW;
97 if (freq < 1705) return FrequencyBand::AM_MW;
98 if (freq < 30000) return FrequencyBand::AM_SW;
99 if (freq < 60000) return FrequencyBand::UNKNOWN;
100 if (freq < 110000) return FrequencyBand::FM;
101 return FrequencyBand::UNKNOWN;
102}
103
Tomasz Wasilczyk06100b32017-12-04 09:53:32 -0800104static bool bothHaveId(const ProgramSelector& a, const ProgramSelector& b,
105 const IdentifierType type) {
106 return hasId(a, type) && hasId(b, type);
107}
108
109static bool haveEqualIds(const ProgramSelector& a, const ProgramSelector& b,
110 const IdentifierType type) {
111 if (!bothHaveId(a, b, type)) return false;
112 /* We should check all Ids of a given type (ie. other AF),
113 * but it doesn't matter for default implementation.
114 */
115 return getId(a, type) == getId(b, type);
116}
117
118static int getHdSubchannel(const ProgramSelector& sel) {
119 auto hdsidext = getId(sel, IdentifierType::HD_STATION_ID_EXT, 0);
120 hdsidext >>= 32; // Station ID number
121 return hdsidext & 0xF; // HD Radio subchannel
122}
123
124bool tunesTo(const ProgramSelector& a, const ProgramSelector& b) {
125 auto type = getType(b.primaryId);
126
127 switch (type) {
128 case IdentifierType::HD_STATION_ID_EXT:
129 case IdentifierType::RDS_PI:
130 case IdentifierType::AMFM_FREQUENCY:
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 && haveEqualIds(a, b, IdentifierType::AMFM_FREQUENCY);
134 case IdentifierType::DAB_SID_EXT:
135 return haveEqualIds(a, b, IdentifierType::DAB_SID_EXT);
136 case IdentifierType::DRMO_SERVICE_ID:
137 return haveEqualIds(a, b, IdentifierType::DRMO_SERVICE_ID);
138 case IdentifierType::SXM_SERVICE_ID:
139 return haveEqualIds(a, b, IdentifierType::SXM_SERVICE_ID);
140 default: // includes all vendor types
141 ALOGW("Unsupported program type: %s", toString(type).c_str());
142 return false;
143 }
144}
145
146static bool maybeGetId(const ProgramSelector& sel, const IdentifierType type, uint64_t* val) {
147 auto itype = static_cast<uint32_t>(type);
148
149 if (sel.primaryId.type == itype) {
150 if (val) *val = sel.primaryId.value;
151 return true;
152 }
153
Tomasz Wasilczykbceb8852017-12-18 13:59:29 -0800154 // TODO(twasilczyk): use IdentifierIterator
Tomasz Wasilczyk06100b32017-12-04 09:53:32 -0800155 // not optimal, but we don't care in default impl
156 for (auto&& id : sel.secondaryIds) {
157 if (id.type == itype) {
158 if (val) *val = id.value;
159 return true;
160 }
161 }
162
163 return false;
164}
165
166bool hasId(const ProgramSelector& sel, const IdentifierType type) {
167 return maybeGetId(sel, type, nullptr);
168}
169
170uint64_t getId(const ProgramSelector& sel, const IdentifierType type) {
171 uint64_t val;
172
173 if (maybeGetId(sel, type, &val)) {
174 return val;
175 }
176
177 ALOGW("Identifier %s not found", toString(type).c_str());
178 return 0;
179}
180
181uint64_t getId(const ProgramSelector& sel, const IdentifierType type, uint64_t defval) {
182 if (!hasId(sel, type)) return defval;
183 return getId(sel, type);
184}
185
Tomasz Wasilczyk31e86322017-12-05 09:36:11 -0800186vector<uint64_t> getAllIds(const ProgramSelector& sel, const IdentifierType type) {
187 vector<uint64_t> ret;
188 auto itype = static_cast<uint32_t>(type);
189
190 if (sel.primaryId.type == itype) ret.push_back(sel.primaryId.value);
191
Tomasz Wasilczykbceb8852017-12-18 13:59:29 -0800192 // TODO(twasilczyk): use IdentifierIterator
Tomasz Wasilczyk31e86322017-12-05 09:36:11 -0800193 for (auto&& id : sel.secondaryIds) {
194 if (id.type == itype) ret.push_back(id.value);
195 }
196
197 return ret;
198}
199
Tomasz Wasilczykbceb8852017-12-18 13:59:29 -0800200bool isSupported(const Properties& prop, const ProgramSelector& sel) {
201 // TODO(twasilczyk): use IdentifierIterator
Tomasz Wasilczyk06100b32017-12-04 09:53:32 -0800202 // Not optimal, but it doesn't matter for default impl nor VTS tests.
Tomasz Wasilczykbceb8852017-12-18 13:59:29 -0800203 for (auto&& idType : prop.supportedIdentifierTypes) {
204 if (hasId(sel, getType(idType))) return true;
Tomasz Wasilczyk06100b32017-12-04 09:53:32 -0800205 }
206 return false;
207}
208
Tomasz Wasilczyk8b70ee42017-12-21 11:51:29 -0800209bool isValid(const ProgramIdentifier& id) {
Tomasz Wasilczyk06100b32017-12-04 09:53:32 -0800210 auto val = id.value;
211 bool valid = true;
212
213 auto expect = [&valid](bool condition, std::string message) {
214 if (!condition) {
215 valid = false;
216 ALOGE("Identifier not valid, expected %s", message.c_str());
217 }
218 };
219
Tomasz Wasilczykbceb8852017-12-18 13:59:29 -0800220 switch (getType(id)) {
Tomasz Wasilczyk4ce63822017-12-21 14:25:54 -0800221 case IdentifierType::INVALID:
222 expect(false, "IdentifierType::INVALID");
223 break;
Tomasz Wasilczyk06100b32017-12-04 09:53:32 -0800224 case IdentifierType::DAB_FREQUENCY:
Tomasz Wasilczyk8b70ee42017-12-21 11:51:29 -0800225 expect(val > 100000u, "f > 100MHz");
226 // fallthrough
227 case IdentifierType::AMFM_FREQUENCY:
Tomasz Wasilczyk06100b32017-12-04 09:53:32 -0800228 case IdentifierType::DRMO_FREQUENCY:
229 expect(val > 100u, "f > 100kHz");
230 expect(val < 10000000u, "f < 10GHz");
231 break;
232 case IdentifierType::RDS_PI:
233 expect(val != 0u, "RDS PI != 0");
234 expect(val <= 0xFFFFu, "16bit id");
235 break;
236 case IdentifierType::HD_STATION_ID_EXT: {
237 auto stationId = val & 0xFFFFFFFF; // 32bit
238 val >>= 32;
239 auto subchannel = val & 0xF; // 4bit
240 val >>= 4;
241 auto freq = val & 0x3FFFF; // 18bit
242 expect(stationId != 0u, "HD station id != 0");
243 expect(subchannel < 8u, "HD subch < 8");
244 expect(freq > 100u, "f > 100kHz");
245 expect(freq < 10000000u, "f < 10GHz");
246 break;
247 }
Tomasz Wasilczykc71624f2017-12-22 10:54:34 -0800248 case IdentifierType::HD_STATION_NAME: {
249 while (val > 0) {
250 auto ch = static_cast<char>(val & 0xFF);
251 val >>= 8;
252 expect((ch >= '0' && ch <= '9') || (ch >= 'A' && ch <= 'Z'),
253 "HD_STATION_NAME does not match [A-Z0-9]+");
254 }
255 break;
256 }
Tomasz Wasilczyk06100b32017-12-04 09:53:32 -0800257 case IdentifierType::DAB_SID_EXT: {
258 auto sid = val & 0xFFFF; // 16bit
259 val >>= 16;
260 auto ecc = val & 0xFF; // 8bit
261 expect(sid != 0u, "DAB SId != 0");
262 expect(ecc >= 0xA0u && ecc <= 0xF6u, "Invalid ECC, see ETSI TS 101 756 V2.1.1");
263 break;
264 }
265 case IdentifierType::DAB_ENSEMBLE:
266 expect(val != 0u, "DAB ensemble != 0");
267 expect(val <= 0xFFFFu, "16bit id");
268 break;
269 case IdentifierType::DAB_SCID:
270 expect(val > 0xFu, "12bit SCId (not 4bit SCIdS)");
271 expect(val <= 0xFFFu, "12bit id");
272 break;
273 case IdentifierType::DRMO_SERVICE_ID:
274 expect(val != 0u, "DRM SId != 0");
275 expect(val <= 0xFFFFFFu, "24bit id");
276 break;
277 case IdentifierType::SXM_SERVICE_ID:
278 expect(val != 0u, "SXM SId != 0");
279 expect(val <= 0xFFFFFFFFu, "32bit id");
280 break;
281 case IdentifierType::SXM_CHANNEL:
282 expect(val < 1000u, "SXM channel < 1000");
283 break;
284 case IdentifierType::VENDOR_START:
285 case IdentifierType::VENDOR_END:
286 // skip
287 break;
288 }
289
290 return valid;
291}
292
Tomasz Wasilczykbceb8852017-12-18 13:59:29 -0800293bool isValid(const ProgramSelector& sel) {
Tomasz Wasilczyk06100b32017-12-04 09:53:32 -0800294 if (!isValid(sel.primaryId)) return false;
Tomasz Wasilczykbceb8852017-12-18 13:59:29 -0800295 // TODO(twasilczyk): use IdentifierIterator
Tomasz Wasilczyk06100b32017-12-04 09:53:32 -0800296 for (auto&& id : sel.secondaryIds) {
297 if (!isValid(id)) return false;
298 }
299 return true;
300}
301
302ProgramIdentifier make_identifier(IdentifierType type, uint64_t value) {
303 return {static_cast<uint32_t>(type), value};
304}
305
306ProgramSelector make_selector_amfm(uint32_t frequency) {
307 ProgramSelector sel = {};
308 sel.primaryId = make_identifier(IdentifierType::AMFM_FREQUENCY, frequency);
309 return sel;
310}
311
312Metadata make_metadata(MetadataKey key, int64_t value) {
313 Metadata meta = {};
314 meta.key = static_cast<uint32_t>(key);
315 meta.intValue = value;
316 return meta;
317}
318
319Metadata make_metadata(MetadataKey key, string value) {
320 Metadata meta = {};
321 meta.key = static_cast<uint32_t>(key);
322 meta.stringValue = value;
323 return meta;
324}
325
Tomasz Wasilczykbceb8852017-12-18 13:59:29 -0800326bool satisfies(const ProgramFilter& filter, const ProgramSelector& sel) {
327 if (filter.identifierTypes.size() > 0) {
328 auto typeEquals = [](const V2_0::ProgramIdentifier& id, uint32_t type) {
329 return id.type == type;
330 };
331 auto it = std::find_first_of(begin(sel), end(sel), filter.identifierTypes.begin(),
332 filter.identifierTypes.end(), typeEquals);
333 if (it == end(sel)) return false;
334 }
335
336 if (filter.identifiers.size() > 0) {
337 auto it = std::find_first_of(begin(sel), end(sel), filter.identifiers.begin(),
338 filter.identifiers.end());
339 if (it == end(sel)) return false;
340 }
341
342 if (!filter.includeCategories) {
343 if (getType(sel.primaryId) == IdentifierType::DAB_ENSEMBLE) return false;
344 }
345
346 return true;
347}
348
349size_t ProgramInfoHasher::operator()(const ProgramInfo& info) const {
350 auto& id = info.selector.primaryId;
351
352 /* This is not the best hash implementation, but good enough for default HAL
353 * implementation and tests. */
354 auto h = std::hash<uint32_t>{}(id.type);
355 h += 0x9e3779b9;
356 h ^= std::hash<uint64_t>{}(id.value);
357
358 return h;
359}
360
361bool ProgramInfoKeyEqual::operator()(const ProgramInfo& info1, const ProgramInfo& info2) const {
362 auto& id1 = info1.selector.primaryId;
363 auto& id2 = info2.selector.primaryId;
364 return id1.type == id2.type && id1.value == id2.value;
365}
366
367void updateProgramList(ProgramInfoSet& list, const ProgramListChunk& chunk) {
368 if (chunk.purge) list.clear();
369
370 list.insert(chunk.modified.begin(), chunk.modified.end());
371
372 for (auto&& id : chunk.removed) {
373 ProgramInfo info = {};
374 info.selector.primaryId = id;
375 list.erase(info);
376 }
377}
378
Tomasz Wasilczykc71624f2017-12-22 10:54:34 -0800379std::optional<std::string> getMetadataString(const V2_0::ProgramInfo& info,
380 const V2_0::MetadataKey key) {
381 auto isKey = [key](const V2_0::Metadata& item) {
382 return static_cast<V2_0::MetadataKey>(item.key) == key;
383 };
384
385 auto it = std::find_if(info.metadata.begin(), info.metadata.end(), isKey);
386 if (it == info.metadata.end()) return std::nullopt;
387
388 return it->stringValue;
389}
390
391V2_0::ProgramIdentifier make_hdradio_station_name(const std::string& name) {
392 constexpr size_t maxlen = 8;
393
394 std::string shortName;
395 shortName.reserve(maxlen);
396
397 auto&& loc = std::locale::classic();
398 for (char ch : name) {
399 if (!std::isalnum(ch, loc)) continue;
400 shortName.push_back(std::toupper(ch, loc));
401 if (shortName.length() >= maxlen) break;
402 }
403
404 uint64_t val = 0;
405 for (auto rit = shortName.rbegin(); rit != shortName.rend(); ++rit) {
406 val <<= 8;
407 val |= static_cast<uint8_t>(*rit);
408 }
409
410 return make_identifier(IdentifierType::HD_STATION_NAME, val);
411}
412
Tomasz Wasilczyk06100b32017-12-04 09:53:32 -0800413} // namespace utils
414} // namespace broadcastradio
415} // namespace hardware
416} // namespace android