blob: 10a155b1365039d1e013689a530c99f56e15b384 [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
92static bool bothHaveId(const ProgramSelector& a, const ProgramSelector& b,
93 const IdentifierType type) {
94 return hasId(a, type) && hasId(b, type);
95}
96
97static bool haveEqualIds(const ProgramSelector& a, const ProgramSelector& b,
98 const IdentifierType type) {
99 if (!bothHaveId(a, b, type)) return false;
100 /* We should check all Ids of a given type (ie. other AF),
101 * but it doesn't matter for default implementation.
102 */
103 return getId(a, type) == getId(b, type);
104}
105
106static int getHdSubchannel(const ProgramSelector& sel) {
107 auto hdsidext = getId(sel, IdentifierType::HD_STATION_ID_EXT, 0);
108 hdsidext >>= 32; // Station ID number
109 return hdsidext & 0xF; // HD Radio subchannel
110}
111
112bool tunesTo(const ProgramSelector& a, const ProgramSelector& b) {
113 auto type = getType(b.primaryId);
114
115 switch (type) {
116 case IdentifierType::HD_STATION_ID_EXT:
117 case IdentifierType::RDS_PI:
118 case IdentifierType::AMFM_FREQUENCY:
119 if (haveEqualIds(a, b, IdentifierType::HD_STATION_ID_EXT)) return true;
120 if (haveEqualIds(a, b, IdentifierType::RDS_PI)) return true;
121 return getHdSubchannel(b) == 0 && haveEqualIds(a, b, IdentifierType::AMFM_FREQUENCY);
122 case IdentifierType::DAB_SID_EXT:
123 return haveEqualIds(a, b, IdentifierType::DAB_SID_EXT);
124 case IdentifierType::DRMO_SERVICE_ID:
125 return haveEqualIds(a, b, IdentifierType::DRMO_SERVICE_ID);
126 case IdentifierType::SXM_SERVICE_ID:
127 return haveEqualIds(a, b, IdentifierType::SXM_SERVICE_ID);
128 default: // includes all vendor types
129 ALOGW("Unsupported program type: %s", toString(type).c_str());
130 return false;
131 }
132}
133
134static bool maybeGetId(const ProgramSelector& sel, const IdentifierType type, uint64_t* val) {
135 auto itype = static_cast<uint32_t>(type);
136
137 if (sel.primaryId.type == itype) {
138 if (val) *val = sel.primaryId.value;
139 return true;
140 }
141
Tomasz Wasilczykbceb8852017-12-18 13:59:29 -0800142 // TODO(twasilczyk): use IdentifierIterator
Tomasz Wasilczyk06100b32017-12-04 09:53:32 -0800143 // not optimal, but we don't care in default impl
144 for (auto&& id : sel.secondaryIds) {
145 if (id.type == itype) {
146 if (val) *val = id.value;
147 return true;
148 }
149 }
150
151 return false;
152}
153
154bool hasId(const ProgramSelector& sel, const IdentifierType type) {
155 return maybeGetId(sel, type, nullptr);
156}
157
158uint64_t getId(const ProgramSelector& sel, const IdentifierType type) {
159 uint64_t val;
160
161 if (maybeGetId(sel, type, &val)) {
162 return val;
163 }
164
165 ALOGW("Identifier %s not found", toString(type).c_str());
166 return 0;
167}
168
169uint64_t getId(const ProgramSelector& sel, const IdentifierType type, uint64_t defval) {
170 if (!hasId(sel, type)) return defval;
171 return getId(sel, type);
172}
173
Tomasz Wasilczyk31e86322017-12-05 09:36:11 -0800174vector<uint64_t> getAllIds(const ProgramSelector& sel, const IdentifierType type) {
175 vector<uint64_t> ret;
176 auto itype = static_cast<uint32_t>(type);
177
178 if (sel.primaryId.type == itype) ret.push_back(sel.primaryId.value);
179
Tomasz Wasilczykbceb8852017-12-18 13:59:29 -0800180 // TODO(twasilczyk): use IdentifierIterator
Tomasz Wasilczyk31e86322017-12-05 09:36:11 -0800181 for (auto&& id : sel.secondaryIds) {
182 if (id.type == itype) ret.push_back(id.value);
183 }
184
185 return ret;
186}
187
Tomasz Wasilczykbceb8852017-12-18 13:59:29 -0800188bool isSupported(const Properties& prop, const ProgramSelector& sel) {
189 // TODO(twasilczyk): use IdentifierIterator
Tomasz Wasilczyk06100b32017-12-04 09:53:32 -0800190 // Not optimal, but it doesn't matter for default impl nor VTS tests.
Tomasz Wasilczykbceb8852017-12-18 13:59:29 -0800191 for (auto&& idType : prop.supportedIdentifierTypes) {
192 if (hasId(sel, getType(idType))) return true;
Tomasz Wasilczyk06100b32017-12-04 09:53:32 -0800193 }
194 return false;
195}
196
197static bool isValid(const ProgramIdentifier& id) {
198 auto val = id.value;
199 bool valid = true;
200
201 auto expect = [&valid](bool condition, std::string message) {
202 if (!condition) {
203 valid = false;
204 ALOGE("Identifier not valid, expected %s", message.c_str());
205 }
206 };
207
Tomasz Wasilczykbceb8852017-12-18 13:59:29 -0800208 switch (getType(id)) {
Tomasz Wasilczyk06100b32017-12-04 09:53:32 -0800209 case IdentifierType::AMFM_FREQUENCY:
210 case IdentifierType::DAB_FREQUENCY:
211 case IdentifierType::DRMO_FREQUENCY:
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 auto stationId = val & 0xFFFFFFFF; // 32bit
221 val >>= 32;
222 auto subchannel = val & 0xF; // 4bit
223 val >>= 4;
224 auto 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::DAB_SID_EXT: {
232 auto sid = val & 0xFFFF; // 16bit
233 val >>= 16;
234 auto ecc = val & 0xFF; // 8bit
235 expect(sid != 0u, "DAB SId != 0");
236 expect(ecc >= 0xA0u && ecc <= 0xF6u, "Invalid ECC, see ETSI TS 101 756 V2.1.1");
237 break;
238 }
239 case IdentifierType::DAB_ENSEMBLE:
240 expect(val != 0u, "DAB ensemble != 0");
241 expect(val <= 0xFFFFu, "16bit id");
242 break;
243 case IdentifierType::DAB_SCID:
244 expect(val > 0xFu, "12bit SCId (not 4bit SCIdS)");
245 expect(val <= 0xFFFu, "12bit id");
246 break;
247 case IdentifierType::DRMO_SERVICE_ID:
248 expect(val != 0u, "DRM SId != 0");
249 expect(val <= 0xFFFFFFu, "24bit id");
250 break;
251 case IdentifierType::SXM_SERVICE_ID:
252 expect(val != 0u, "SXM SId != 0");
253 expect(val <= 0xFFFFFFFFu, "32bit id");
254 break;
255 case IdentifierType::SXM_CHANNEL:
256 expect(val < 1000u, "SXM channel < 1000");
257 break;
258 case IdentifierType::VENDOR_START:
259 case IdentifierType::VENDOR_END:
260 // skip
261 break;
262 }
263
264 return valid;
265}
266
Tomasz Wasilczykbceb8852017-12-18 13:59:29 -0800267bool isValid(const ProgramSelector& sel) {
Tomasz Wasilczyk06100b32017-12-04 09:53:32 -0800268 if (!isValid(sel.primaryId)) return false;
Tomasz Wasilczykbceb8852017-12-18 13:59:29 -0800269 // TODO(twasilczyk): use IdentifierIterator
Tomasz Wasilczyk06100b32017-12-04 09:53:32 -0800270 for (auto&& id : sel.secondaryIds) {
271 if (!isValid(id)) return false;
272 }
273 return true;
274}
275
276ProgramIdentifier make_identifier(IdentifierType type, uint64_t value) {
277 return {static_cast<uint32_t>(type), value};
278}
279
280ProgramSelector make_selector_amfm(uint32_t frequency) {
281 ProgramSelector sel = {};
282 sel.primaryId = make_identifier(IdentifierType::AMFM_FREQUENCY, frequency);
283 return sel;
284}
285
286Metadata make_metadata(MetadataKey key, int64_t value) {
287 Metadata meta = {};
288 meta.key = static_cast<uint32_t>(key);
289 meta.intValue = value;
290 return meta;
291}
292
293Metadata make_metadata(MetadataKey key, string value) {
294 Metadata meta = {};
295 meta.key = static_cast<uint32_t>(key);
296 meta.stringValue = value;
297 return meta;
298}
299
Tomasz Wasilczykbceb8852017-12-18 13:59:29 -0800300bool satisfies(const ProgramFilter& filter, const ProgramSelector& sel) {
301 if (filter.identifierTypes.size() > 0) {
302 auto typeEquals = [](const V2_0::ProgramIdentifier& id, uint32_t type) {
303 return id.type == type;
304 };
305 auto it = std::find_first_of(begin(sel), end(sel), filter.identifierTypes.begin(),
306 filter.identifierTypes.end(), typeEquals);
307 if (it == end(sel)) return false;
308 }
309
310 if (filter.identifiers.size() > 0) {
311 auto it = std::find_first_of(begin(sel), end(sel), filter.identifiers.begin(),
312 filter.identifiers.end());
313 if (it == end(sel)) return false;
314 }
315
316 if (!filter.includeCategories) {
317 if (getType(sel.primaryId) == IdentifierType::DAB_ENSEMBLE) return false;
318 }
319
320 return true;
321}
322
323size_t ProgramInfoHasher::operator()(const ProgramInfo& info) const {
324 auto& id = info.selector.primaryId;
325
326 /* This is not the best hash implementation, but good enough for default HAL
327 * implementation and tests. */
328 auto h = std::hash<uint32_t>{}(id.type);
329 h += 0x9e3779b9;
330 h ^= std::hash<uint64_t>{}(id.value);
331
332 return h;
333}
334
335bool ProgramInfoKeyEqual::operator()(const ProgramInfo& info1, const ProgramInfo& info2) const {
336 auto& id1 = info1.selector.primaryId;
337 auto& id2 = info2.selector.primaryId;
338 return id1.type == id2.type && id1.value == id2.value;
339}
340
341void updateProgramList(ProgramInfoSet& list, const ProgramListChunk& chunk) {
342 if (chunk.purge) list.clear();
343
344 list.insert(chunk.modified.begin(), chunk.modified.end());
345
346 for (auto&& id : chunk.removed) {
347 ProgramInfo info = {};
348 info.selector.primaryId = id;
349 list.erase(info);
350 }
351}
352
Tomasz Wasilczyk06100b32017-12-04 09:53:32 -0800353} // namespace utils
354} // namespace broadcastradio
355} // namespace hardware
356} // namespace android