1 /*
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 
18 #include <broadcastradio-utils-2x/Utils.h>
19 
20 #include <android-base/logging.h>
21 
22 namespace android {
23 namespace hardware {
24 namespace broadcastradio {
25 namespace utils {
26 
27 using V2_0::IdentifierType;
28 using V2_0::Metadata;
29 using V2_0::MetadataKey;
30 using V2_0::ProgramFilter;
31 using V2_0::ProgramIdentifier;
32 using V2_0::ProgramInfo;
33 using V2_0::ProgramListChunk;
34 using V2_0::ProgramSelector;
35 using V2_0::Properties;
36 
37 using std::string;
38 using std::vector;
39 
getType(uint32_t typeAsInt)40 IdentifierType getType(uint32_t typeAsInt) {
41     return static_cast<IdentifierType>(typeAsInt);
42 }
43 
getType(const ProgramIdentifier & id)44 IdentifierType getType(const ProgramIdentifier& id) {
45     return getType(id.type);
46 }
47 
IdentifierIterator(const V2_0::ProgramSelector & sel)48 IdentifierIterator::IdentifierIterator(const V2_0::ProgramSelector& sel)
49     : IdentifierIterator(sel, 0) {}
50 
IdentifierIterator(const V2_0::ProgramSelector & sel,size_t pos)51 IdentifierIterator::IdentifierIterator(const V2_0::ProgramSelector& sel, size_t pos)
52     : mSel(sel), mPos(pos) {}
53 
operator ++(int)54 IdentifierIterator IdentifierIterator::operator++(int) {
55     auto i = *this;
56     mPos++;
57     return i;
58 }
59 
operator ++()60 IdentifierIterator& IdentifierIterator::operator++() {
61     ++mPos;
62     return *this;
63 }
64 
operator *() const65 IdentifierIterator::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 
operator ==(const IdentifierIterator & rhs) const73 bool 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 
getBand(uint64_t freq)82 FrequencyBand 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 
bothHaveId(const ProgramSelector & a,const ProgramSelector & b,const IdentifierType type)94 static bool bothHaveId(const ProgramSelector& a, const ProgramSelector& b,
95                        const IdentifierType type) {
96     return hasId(a, type) && hasId(b, type);
97 }
98 
haveEqualIds(const ProgramSelector & a,const ProgramSelector & b,const IdentifierType type)99 static 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 
getHdSubchannel(const ProgramSelector & sel)108 static 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 
tunesTo(const ProgramSelector & a,const ProgramSelector & b)114 bool 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
131             LOG(WARNING) << "unsupported program type: " << toString(type);
132             return false;
133     }
134 }
135 
maybeGetId(const ProgramSelector & sel,const IdentifierType type,uint64_t * val)136 static 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 
144     // TODO(twasilczyk): use IdentifierIterator
145     // 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 
hasId(const ProgramSelector & sel,const IdentifierType type)156 bool hasId(const ProgramSelector& sel, const IdentifierType type) {
157     return maybeGetId(sel, type, nullptr);
158 }
159 
getId(const ProgramSelector & sel,const IdentifierType type)160 uint64_t getId(const ProgramSelector& sel, const IdentifierType type) {
161     uint64_t val;
162 
163     if (maybeGetId(sel, type, &val)) {
164         return val;
165     }
166 
167     LOG(WARNING) << "identifier not found: " << toString(type);
168     return 0;
169 }
170 
getId(const ProgramSelector & sel,const IdentifierType type,uint64_t defval)171 uint64_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 
getAllIds(const ProgramSelector & sel,const IdentifierType type)176 vector<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 
182     // TODO(twasilczyk): use IdentifierIterator
183     for (auto&& id : sel.secondaryIds) {
184         if (id.type == itype) ret.push_back(id.value);
185     }
186 
187     return ret;
188 }
189 
isSupported(const Properties & prop,const ProgramSelector & sel)190 bool isSupported(const Properties& prop, const ProgramSelector& sel) {
191     // TODO(twasilczyk): use IdentifierIterator
192     // Not optimal, but it doesn't matter for default impl nor VTS tests.
193     for (auto&& idType : prop.supportedIdentifierTypes) {
194         if (hasId(sel, getType(idType))) return true;
195     }
196     return false;
197 }
198 
isValid(const ProgramIdentifier & id)199 bool isValid(const ProgramIdentifier& id) {
200     auto val = id.value;
201     bool valid = true;
202 
203     auto expect = [&valid](bool condition, std::string message) {
204         if (!condition) {
205             valid = false;
206             LOG(ERROR) << "identifier not valid, expected " << message;
207         }
208     };
209 
210     switch (getType(id)) {
211         case IdentifierType::INVALID:
212             expect(false, "IdentifierType::INVALID");
213             break;
214         case IdentifierType::DAB_FREQUENCY:
215             expect(val > 100000u, "f > 100MHz");
216             [[fallthrough]];
217         case IdentifierType::AMFM_FREQUENCY:
218         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         }
238         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         }
247         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 
isValid(const ProgramSelector & sel)283 bool isValid(const ProgramSelector& sel) {
284     if (!isValid(sel.primaryId)) return false;
285     // TODO(twasilczyk): use IdentifierIterator
286     for (auto&& id : sel.secondaryIds) {
287         if (!isValid(id)) return false;
288     }
289     return true;
290 }
291 
make_identifier(IdentifierType type,uint64_t value)292 ProgramIdentifier make_identifier(IdentifierType type, uint64_t value) {
293     return {static_cast<uint32_t>(type), value};
294 }
295 
make_selector_amfm(uint32_t frequency)296 ProgramSelector make_selector_amfm(uint32_t frequency) {
297     ProgramSelector sel = {};
298     sel.primaryId = make_identifier(IdentifierType::AMFM_FREQUENCY, frequency);
299     return sel;
300 }
301 
make_selector_dab(uint32_t sidExt,uint32_t ensemble)302 ProgramSelector 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 
make_metadata(MetadataKey key,int64_t value)316 Metadata 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 
make_metadata(MetadataKey key,string value)323 Metadata 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 
satisfies(const ProgramFilter & filter,const ProgramSelector & sel)330 bool 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 
operator ()(const ProgramInfo & info) const353 size_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 
operator ()(const ProgramInfo & info1,const ProgramInfo & info2) const365 bool 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 
updateProgramList(ProgramInfoSet & list,const ProgramListChunk & chunk)371 void 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 
getMetadataString(const V2_0::ProgramInfo & info,const V2_0::MetadataKey key)383 std::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 
make_hdradio_station_name(const std::string & name)395 V2_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 
417 }  // namespace utils
418 
419 namespace V2_0 {
420 
begin(const ProgramSelector & sel)421 utils::IdentifierIterator begin(const ProgramSelector& sel) {
422     return utils::IdentifierIterator(sel);
423 }
424 
end(const ProgramSelector & sel)425 utils::IdentifierIterator end(const ProgramSelector& sel) {
426     return utils::IdentifierIterator(sel) + 1 /* primary id */ + sel.secondaryIds.size();
427 }
428 
429 }  // namespace V2_0
430 }  // namespace broadcastradio
431 }  // namespace hardware
432 }  // namespace android
433