blob: 43f272efdba4fe2345077bcc12353e30e93807ec [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"
Tomasz Wasilczyk06100b32017-12-04 09:53:32 -080017
18#include <broadcastradio-utils-2x/Utils.h>
19
Tomasz Wasilczykbceb8852017-12-18 13:59:29 -080020#include <android-base/logging.h>
Tomasz Wasilczyk06100b32017-12-04 09:53:32 -080021
22namespace android {
23namespace hardware {
24namespace broadcastradio {
25namespace utils {
26
27using V2_0::IdentifierType;
28using V2_0::Metadata;
29using V2_0::MetadataKey;
Tomasz Wasilczykbceb8852017-12-18 13:59:29 -080030using V2_0::ProgramFilter;
Tomasz Wasilczyk06100b32017-12-04 09:53:32 -080031using V2_0::ProgramIdentifier;
Tomasz Wasilczykbceb8852017-12-18 13:59:29 -080032using V2_0::ProgramInfo;
33using V2_0::ProgramListChunk;
Tomasz Wasilczyk06100b32017-12-04 09:53:32 -080034using V2_0::ProgramSelector;
Tomasz Wasilczykbceb8852017-12-18 13:59:29 -080035using V2_0::Properties;
Tomasz Wasilczyk06100b32017-12-04 09:53:32 -080036
37using std::string;
Tomasz Wasilczyk31e86322017-12-05 09:36:11 -080038using std::vector;
Tomasz Wasilczyk06100b32017-12-04 09:53:32 -080039
Tomasz Wasilczykbceb8852017-12-18 13:59:29 -080040IdentifierType getType(uint32_t typeAsInt) {
41 return static_cast<IdentifierType>(typeAsInt);
42}
43
Tomasz Wasilczyk06100b32017-12-04 09:53:32 -080044IdentifierType getType(const ProgramIdentifier& id) {
Tomasz Wasilczykbceb8852017-12-18 13:59:29 -080045 return getType(id.type);
46}
47
48IdentifierIterator::IdentifierIterator(const V2_0::ProgramSelector& sel)
49 : IdentifierIterator(sel, 0) {}
50
51IdentifierIterator::IdentifierIterator(const V2_0::ProgramSelector& sel, size_t pos)
52 : mSel(sel), mPos(pos) {}
53
54IdentifierIterator IdentifierIterator::operator++(int) {
55 auto i = *this;
56 mPos++;
57 return i;
58}
59
60IdentifierIterator& IdentifierIterator::operator++() {
61 ++mPos;
62 return *this;
63}
64
65IdentifierIterator::ref_type IdentifierIterator::operator*() const {
66 if (mPos == 0) return sel().primaryId;
67
68 // mPos is 1-based for secondary identifiers
69 DCHECK(mPos <= sel().secondaryIds.size());
70 return sel().secondaryIds[mPos - 1];
71}
72
73bool IdentifierIterator::operator==(const IdentifierIterator& rhs) const {
74 // Check, if both iterators points at the same selector.
75 if (reinterpret_cast<uintptr_t>(&sel()) != reinterpret_cast<uintptr_t>(&rhs.sel())) {
76 return false;
77 }
78
79 return mPos == rhs.mPos;
80}
81
Tomasz Wasilczyk8b70ee42017-12-21 11:51:29 -080082FrequencyBand getBand(uint64_t freq) {
83 // keep in sync with
84 // frameworks/base/services/core/java/com/android/server/broadcastradio/hal2/Utils.java
85 if (freq < 30) return FrequencyBand::UNKNOWN;
86 if (freq < 500) return FrequencyBand::AM_LW;
87 if (freq < 1705) return FrequencyBand::AM_MW;
88 if (freq < 30000) return FrequencyBand::AM_SW;
89 if (freq < 60000) return FrequencyBand::UNKNOWN;
90 if (freq < 110000) return FrequencyBand::FM;
91 return FrequencyBand::UNKNOWN;
92}
93
Tomasz Wasilczyk06100b32017-12-04 09:53:32 -080094static bool bothHaveId(const ProgramSelector& a, const ProgramSelector& b,
95 const IdentifierType type) {
96 return hasId(a, type) && hasId(b, type);
97}
98
99static bool haveEqualIds(const ProgramSelector& a, const ProgramSelector& b,
100 const IdentifierType type) {
101 if (!bothHaveId(a, b, type)) return false;
102 /* We should check all Ids of a given type (ie. other AF),
103 * but it doesn't matter for default implementation.
104 */
105 return getId(a, type) == getId(b, type);
106}
107
108static int getHdSubchannel(const ProgramSelector& sel) {
109 auto hdsidext = getId(sel, IdentifierType::HD_STATION_ID_EXT, 0);
110 hdsidext >>= 32; // Station ID number
111 return hdsidext & 0xF; // HD Radio subchannel
112}
113
114bool tunesTo(const ProgramSelector& a, const ProgramSelector& b) {
115 auto type = getType(b.primaryId);
116
117 switch (type) {
118 case IdentifierType::HD_STATION_ID_EXT:
119 case IdentifierType::RDS_PI:
120 case IdentifierType::AMFM_FREQUENCY:
121 if (haveEqualIds(a, b, IdentifierType::HD_STATION_ID_EXT)) return true;
122 if (haveEqualIds(a, b, IdentifierType::RDS_PI)) return true;
123 return getHdSubchannel(b) == 0 && haveEqualIds(a, b, IdentifierType::AMFM_FREQUENCY);
124 case IdentifierType::DAB_SID_EXT:
125 return haveEqualIds(a, b, IdentifierType::DAB_SID_EXT);
126 case IdentifierType::DRMO_SERVICE_ID:
127 return haveEqualIds(a, b, IdentifierType::DRMO_SERVICE_ID);
128 case IdentifierType::SXM_SERVICE_ID:
129 return haveEqualIds(a, b, IdentifierType::SXM_SERVICE_ID);
130 default: // includes all vendor types
Tomasz Wasilczyk84ec4e12018-11-13 11:26:23 -0800131 LOG(WARNING) << "unsupported program type: " << toString(type);
Tomasz Wasilczyk06100b32017-12-04 09:53:32 -0800132 return false;
133 }
134}
135
136static bool maybeGetId(const ProgramSelector& sel, const IdentifierType type, uint64_t* val) {
137 auto itype = static_cast<uint32_t>(type);
138
139 if (sel.primaryId.type == itype) {
140 if (val) *val = sel.primaryId.value;
141 return true;
142 }
143
Tomasz Wasilczykbceb8852017-12-18 13:59:29 -0800144 // TODO(twasilczyk): use IdentifierIterator
Tomasz Wasilczyk06100b32017-12-04 09:53:32 -0800145 // not optimal, but we don't care in default impl
146 for (auto&& id : sel.secondaryIds) {
147 if (id.type == itype) {
148 if (val) *val = id.value;
149 return true;
150 }
151 }
152
153 return false;
154}
155
156bool hasId(const ProgramSelector& sel, const IdentifierType type) {
157 return maybeGetId(sel, type, nullptr);
158}
159
160uint64_t getId(const ProgramSelector& sel, const IdentifierType type) {
161 uint64_t val;
162
163 if (maybeGetId(sel, type, &val)) {
164 return val;
165 }
166
Tomasz Wasilczyk84ec4e12018-11-13 11:26:23 -0800167 LOG(WARNING) << "identifier not found: " << toString(type);
Tomasz Wasilczyk06100b32017-12-04 09:53:32 -0800168 return 0;
169}
170
171uint64_t getId(const ProgramSelector& sel, const IdentifierType type, uint64_t defval) {
172 if (!hasId(sel, type)) return defval;
173 return getId(sel, type);
174}
175
Tomasz Wasilczyk31e86322017-12-05 09:36:11 -0800176vector<uint64_t> getAllIds(const ProgramSelector& sel, const IdentifierType type) {
177 vector<uint64_t> ret;
178 auto itype = static_cast<uint32_t>(type);
179
180 if (sel.primaryId.type == itype) ret.push_back(sel.primaryId.value);
181
Tomasz Wasilczykbceb8852017-12-18 13:59:29 -0800182 // TODO(twasilczyk): use IdentifierIterator
Tomasz Wasilczyk31e86322017-12-05 09:36:11 -0800183 for (auto&& id : sel.secondaryIds) {
184 if (id.type == itype) ret.push_back(id.value);
185 }
186
187 return ret;
188}
189
Tomasz Wasilczykbceb8852017-12-18 13:59:29 -0800190bool isSupported(const Properties& prop, const ProgramSelector& sel) {
191 // TODO(twasilczyk): use IdentifierIterator
Tomasz Wasilczyk06100b32017-12-04 09:53:32 -0800192 // Not optimal, but it doesn't matter for default impl nor VTS tests.
Tomasz Wasilczykbceb8852017-12-18 13:59:29 -0800193 for (auto&& idType : prop.supportedIdentifierTypes) {
194 if (hasId(sel, getType(idType))) return true;
Tomasz Wasilczyk06100b32017-12-04 09:53:32 -0800195 }
196 return false;
197}
198
Tomasz Wasilczyk8b70ee42017-12-21 11:51:29 -0800199bool isValid(const ProgramIdentifier& id) {
Tomasz Wasilczyk06100b32017-12-04 09:53:32 -0800200 auto val = id.value;
201 bool valid = true;
202
203 auto expect = [&valid](bool condition, std::string message) {
204 if (!condition) {
205 valid = false;
Tomasz Wasilczyk84ec4e12018-11-13 11:26:23 -0800206 LOG(ERROR) << "identifier not valid, expected " << message;
Tomasz Wasilczyk06100b32017-12-04 09:53:32 -0800207 }
208 };
209
Tomasz Wasilczykbceb8852017-12-18 13:59:29 -0800210 switch (getType(id)) {
Tomasz Wasilczyk4ce63822017-12-21 14:25:54 -0800211 case IdentifierType::INVALID:
212 expect(false, "IdentifierType::INVALID");
213 break;
Tomasz Wasilczyk06100b32017-12-04 09:53:32 -0800214 case IdentifierType::DAB_FREQUENCY:
Tomasz Wasilczyk8b70ee42017-12-21 11:51:29 -0800215 expect(val > 100000u, "f > 100MHz");
Chih-Hung Hsieh63f4fe62018-10-19 14:24:58 -0700216 [[fallthrough]];
Tomasz Wasilczyk8b70ee42017-12-21 11:51:29 -0800217 case IdentifierType::AMFM_FREQUENCY:
Tomasz Wasilczyk06100b32017-12-04 09:53:32 -0800218 case IdentifierType::DRMO_FREQUENCY:
219 expect(val > 100u, "f > 100kHz");
220 expect(val < 10000000u, "f < 10GHz");
221 break;
222 case IdentifierType::RDS_PI:
223 expect(val != 0u, "RDS PI != 0");
224 expect(val <= 0xFFFFu, "16bit id");
225 break;
226 case IdentifierType::HD_STATION_ID_EXT: {
227 auto stationId = val & 0xFFFFFFFF; // 32bit
228 val >>= 32;
229 auto subchannel = val & 0xF; // 4bit
230 val >>= 4;
231 auto freq = val & 0x3FFFF; // 18bit
232 expect(stationId != 0u, "HD station id != 0");
233 expect(subchannel < 8u, "HD subch < 8");
234 expect(freq > 100u, "f > 100kHz");
235 expect(freq < 10000000u, "f < 10GHz");
236 break;
237 }
Tomasz Wasilczykc71624f2017-12-22 10:54:34 -0800238 case IdentifierType::HD_STATION_NAME: {
239 while (val > 0) {
240 auto ch = static_cast<char>(val & 0xFF);
241 val >>= 8;
242 expect((ch >= '0' && ch <= '9') || (ch >= 'A' && ch <= 'Z'),
243 "HD_STATION_NAME does not match [A-Z0-9]+");
244 }
245 break;
246 }
Tomasz Wasilczyk06100b32017-12-04 09:53:32 -0800247 case IdentifierType::DAB_SID_EXT: {
248 auto sid = val & 0xFFFF; // 16bit
249 val >>= 16;
250 auto ecc = val & 0xFF; // 8bit
251 expect(sid != 0u, "DAB SId != 0");
252 expect(ecc >= 0xA0u && ecc <= 0xF6u, "Invalid ECC, see ETSI TS 101 756 V2.1.1");
253 break;
254 }
255 case IdentifierType::DAB_ENSEMBLE:
256 expect(val != 0u, "DAB ensemble != 0");
257 expect(val <= 0xFFFFu, "16bit id");
258 break;
259 case IdentifierType::DAB_SCID:
260 expect(val > 0xFu, "12bit SCId (not 4bit SCIdS)");
261 expect(val <= 0xFFFu, "12bit id");
262 break;
263 case IdentifierType::DRMO_SERVICE_ID:
264 expect(val != 0u, "DRM SId != 0");
265 expect(val <= 0xFFFFFFu, "24bit id");
266 break;
267 case IdentifierType::SXM_SERVICE_ID:
268 expect(val != 0u, "SXM SId != 0");
269 expect(val <= 0xFFFFFFFFu, "32bit id");
270 break;
271 case IdentifierType::SXM_CHANNEL:
272 expect(val < 1000u, "SXM channel < 1000");
273 break;
274 case IdentifierType::VENDOR_START:
275 case IdentifierType::VENDOR_END:
276 // skip
277 break;
278 }
279
280 return valid;
281}
282
Tomasz Wasilczykbceb8852017-12-18 13:59:29 -0800283bool isValid(const ProgramSelector& sel) {
Tomasz Wasilczyk06100b32017-12-04 09:53:32 -0800284 if (!isValid(sel.primaryId)) return false;
Tomasz Wasilczykbceb8852017-12-18 13:59:29 -0800285 // TODO(twasilczyk): use IdentifierIterator
Tomasz Wasilczyk06100b32017-12-04 09:53:32 -0800286 for (auto&& id : sel.secondaryIds) {
287 if (!isValid(id)) return false;
288 }
289 return true;
290}
291
292ProgramIdentifier make_identifier(IdentifierType type, uint64_t value) {
293 return {static_cast<uint32_t>(type), value};
294}
295
296ProgramSelector make_selector_amfm(uint32_t frequency) {
297 ProgramSelector sel = {};
298 sel.primaryId = make_identifier(IdentifierType::AMFM_FREQUENCY, frequency);
299 return sel;
300}
301
Allief62374d2019-02-19 16:21:51 -0800302ProgramSelector make_selector_dab(uint32_t sidExt, uint32_t ensemble) {
303 ProgramSelector sel = {};
304 // TODO(maryabad): Have a helper function to create the sidExt instead of
305 // passing the whole identifier here. Something like make_dab_sid_ext.
306 sel.primaryId = make_identifier(IdentifierType::DAB_SID_EXT, sidExt);
307 hidl_vec<ProgramIdentifier> secondaryIds = {
308 make_identifier(IdentifierType::DAB_ENSEMBLE, ensemble),
309 // TODO(maryabad): Include frequency here when the helper method to
310 // translate between ensemble and frequency is implemented.
311 };
312 sel.secondaryIds = secondaryIds;
313 return sel;
314}
315
Tomasz Wasilczyk06100b32017-12-04 09:53:32 -0800316Metadata make_metadata(MetadataKey key, int64_t value) {
317 Metadata meta = {};
318 meta.key = static_cast<uint32_t>(key);
319 meta.intValue = value;
320 return meta;
321}
322
323Metadata make_metadata(MetadataKey key, string value) {
324 Metadata meta = {};
325 meta.key = static_cast<uint32_t>(key);
326 meta.stringValue = value;
327 return meta;
328}
329
Tomasz Wasilczykbceb8852017-12-18 13:59:29 -0800330bool satisfies(const ProgramFilter& filter, const ProgramSelector& sel) {
331 if (filter.identifierTypes.size() > 0) {
332 auto typeEquals = [](const V2_0::ProgramIdentifier& id, uint32_t type) {
333 return id.type == type;
334 };
335 auto it = std::find_first_of(begin(sel), end(sel), filter.identifierTypes.begin(),
336 filter.identifierTypes.end(), typeEquals);
337 if (it == end(sel)) return false;
338 }
339
340 if (filter.identifiers.size() > 0) {
341 auto it = std::find_first_of(begin(sel), end(sel), filter.identifiers.begin(),
342 filter.identifiers.end());
343 if (it == end(sel)) return false;
344 }
345
346 if (!filter.includeCategories) {
347 if (getType(sel.primaryId) == IdentifierType::DAB_ENSEMBLE) return false;
348 }
349
350 return true;
351}
352
353size_t ProgramInfoHasher::operator()(const ProgramInfo& info) const {
354 auto& id = info.selector.primaryId;
355
356 /* This is not the best hash implementation, but good enough for default HAL
357 * implementation and tests. */
358 auto h = std::hash<uint32_t>{}(id.type);
359 h += 0x9e3779b9;
360 h ^= std::hash<uint64_t>{}(id.value);
361
362 return h;
363}
364
365bool ProgramInfoKeyEqual::operator()(const ProgramInfo& info1, const ProgramInfo& info2) const {
366 auto& id1 = info1.selector.primaryId;
367 auto& id2 = info2.selector.primaryId;
368 return id1.type == id2.type && id1.value == id2.value;
369}
370
371void updateProgramList(ProgramInfoSet& list, const ProgramListChunk& chunk) {
372 if (chunk.purge) list.clear();
373
374 list.insert(chunk.modified.begin(), chunk.modified.end());
375
376 for (auto&& id : chunk.removed) {
377 ProgramInfo info = {};
378 info.selector.primaryId = id;
379 list.erase(info);
380 }
381}
382
Tomasz Wasilczykc71624f2017-12-22 10:54:34 -0800383std::optional<std::string> getMetadataString(const V2_0::ProgramInfo& info,
384 const V2_0::MetadataKey key) {
385 auto isKey = [key](const V2_0::Metadata& item) {
386 return static_cast<V2_0::MetadataKey>(item.key) == key;
387 };
388
389 auto it = std::find_if(info.metadata.begin(), info.metadata.end(), isKey);
390 if (it == info.metadata.end()) return std::nullopt;
391
392 return it->stringValue;
393}
394
395V2_0::ProgramIdentifier make_hdradio_station_name(const std::string& name) {
396 constexpr size_t maxlen = 8;
397
398 std::string shortName;
399 shortName.reserve(maxlen);
400
401 auto&& loc = std::locale::classic();
402 for (char ch : name) {
403 if (!std::isalnum(ch, loc)) continue;
404 shortName.push_back(std::toupper(ch, loc));
405 if (shortName.length() >= maxlen) break;
406 }
407
408 uint64_t val = 0;
409 for (auto rit = shortName.rbegin(); rit != shortName.rend(); ++rit) {
410 val <<= 8;
411 val |= static_cast<uint8_t>(*rit);
412 }
413
414 return make_identifier(IdentifierType::HD_STATION_NAME, val);
415}
416
Tomasz Wasilczyk06100b32017-12-04 09:53:32 -0800417} // namespace utils
Tomasz Wasilczykcea64962018-05-23 09:56:58 -0700418
419namespace V2_0 {
420
421utils::IdentifierIterator begin(const ProgramSelector& sel) {
422 return utils::IdentifierIterator(sel);
423}
424
425utils::IdentifierIterator end(const ProgramSelector& sel) {
426 return utils::IdentifierIterator(sel) + 1 /* primary id */ + sel.secondaryIds.size();
427}
428
429} // namespace V2_0
Tomasz Wasilczyk06100b32017-12-04 09:53:32 -0800430} // namespace broadcastradio
431} // namespace hardware
432} // namespace android