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//#define LOG_NDEBUG 0
18
19#include <broadcastradio-utils-2x/Utils.h>
20
21#include <android-base/logging.h>
22#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;
32using V2_0::ProgramFilter;
33using V2_0::ProgramIdentifier;
34using V2_0::ProgramInfo;
35using V2_0::ProgramListChunk;
36using V2_0::ProgramSelector;
37using V2_0::Properties;
38
39using std::string;
40using std::vector;
41
42IdentifierType getType(uint32_t typeAsInt) {
43    return static_cast<IdentifierType>(typeAsInt);
44}
45
46IdentifierType getType(const ProgramIdentifier& id) {
47    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
84FrequencyBand getBand(uint64_t freq) {
85    // keep in sync with
86    // frameworks/base/services/core/java/com/android/server/broadcastradio/hal2/Utils.java
87    if (freq < 30) return FrequencyBand::UNKNOWN;
88    if (freq < 500) return FrequencyBand::AM_LW;
89    if (freq < 1705) return FrequencyBand::AM_MW;
90    if (freq < 30000) return FrequencyBand::AM_SW;
91    if (freq < 60000) return FrequencyBand::UNKNOWN;
92    if (freq < 110000) return FrequencyBand::FM;
93    return FrequencyBand::UNKNOWN;
94}
95
96static bool bothHaveId(const ProgramSelector& a, const ProgramSelector& b,
97                       const IdentifierType type) {
98    return hasId(a, type) && hasId(b, type);
99}
100
101static bool haveEqualIds(const ProgramSelector& a, const ProgramSelector& b,
102                         const IdentifierType type) {
103    if (!bothHaveId(a, b, type)) return false;
104    /* We should check all Ids of a given type (ie. other AF),
105     * but it doesn't matter for default implementation.
106     */
107    return getId(a, type) == getId(b, type);
108}
109
110static int getHdSubchannel(const ProgramSelector& sel) {
111    auto hdsidext = getId(sel, IdentifierType::HD_STATION_ID_EXT, 0);
112    hdsidext >>= 32;        // Station ID number
113    return hdsidext & 0xF;  // HD Radio subchannel
114}
115
116bool tunesTo(const ProgramSelector& a, const ProgramSelector& b) {
117    auto type = getType(b.primaryId);
118
119    switch (type) {
120        case IdentifierType::HD_STATION_ID_EXT:
121        case IdentifierType::RDS_PI:
122        case IdentifierType::AMFM_FREQUENCY:
123            if (haveEqualIds(a, b, IdentifierType::HD_STATION_ID_EXT)) return true;
124            if (haveEqualIds(a, b, IdentifierType::RDS_PI)) return true;
125            return getHdSubchannel(b) == 0 && haveEqualIds(a, b, IdentifierType::AMFM_FREQUENCY);
126        case IdentifierType::DAB_SID_EXT:
127            return haveEqualIds(a, b, IdentifierType::DAB_SID_EXT);
128        case IdentifierType::DRMO_SERVICE_ID:
129            return haveEqualIds(a, b, IdentifierType::DRMO_SERVICE_ID);
130        case IdentifierType::SXM_SERVICE_ID:
131            return haveEqualIds(a, b, IdentifierType::SXM_SERVICE_ID);
132        default:  // includes all vendor types
133            ALOGW("Unsupported program type: %s", toString(type).c_str());
134            return false;
135    }
136}
137
138static bool maybeGetId(const ProgramSelector& sel, const IdentifierType type, uint64_t* val) {
139    auto itype = static_cast<uint32_t>(type);
140
141    if (sel.primaryId.type == itype) {
142        if (val) *val = sel.primaryId.value;
143        return true;
144    }
145
146    // TODO(twasilczyk): use IdentifierIterator
147    // not optimal, but we don't care in default impl
148    for (auto&& id : sel.secondaryIds) {
149        if (id.type == itype) {
150            if (val) *val = id.value;
151            return true;
152        }
153    }
154
155    return false;
156}
157
158bool hasId(const ProgramSelector& sel, const IdentifierType type) {
159    return maybeGetId(sel, type, nullptr);
160}
161
162uint64_t getId(const ProgramSelector& sel, const IdentifierType type) {
163    uint64_t val;
164
165    if (maybeGetId(sel, type, &val)) {
166        return val;
167    }
168
169    ALOGW("Identifier %s not found", toString(type).c_str());
170    return 0;
171}
172
173uint64_t getId(const ProgramSelector& sel, const IdentifierType type, uint64_t defval) {
174    if (!hasId(sel, type)) return defval;
175    return getId(sel, type);
176}
177
178vector<uint64_t> getAllIds(const ProgramSelector& sel, const IdentifierType type) {
179    vector<uint64_t> ret;
180    auto itype = static_cast<uint32_t>(type);
181
182    if (sel.primaryId.type == itype) ret.push_back(sel.primaryId.value);
183
184    // TODO(twasilczyk): use IdentifierIterator
185    for (auto&& id : sel.secondaryIds) {
186        if (id.type == itype) ret.push_back(id.value);
187    }
188
189    return ret;
190}
191
192bool isSupported(const Properties& prop, const ProgramSelector& sel) {
193    // TODO(twasilczyk): use IdentifierIterator
194    // Not optimal, but it doesn't matter for default impl nor VTS tests.
195    for (auto&& idType : prop.supportedIdentifierTypes) {
196        if (hasId(sel, getType(idType))) return true;
197    }
198    return false;
199}
200
201bool isValid(const ProgramIdentifier& id) {
202    auto val = id.value;
203    bool valid = true;
204
205    auto expect = [&valid](bool condition, std::string message) {
206        if (!condition) {
207            valid = false;
208            ALOGE("Identifier not valid, expected %s", message.c_str());
209        }
210    };
211
212    switch (getType(id)) {
213        case IdentifierType::INVALID:
214            expect(false, "IdentifierType::INVALID");
215            break;
216        case IdentifierType::DAB_FREQUENCY:
217            expect(val > 100000u, "f > 100MHz");
218        // fallthrough
219        case IdentifierType::AMFM_FREQUENCY:
220        case IdentifierType::DRMO_FREQUENCY:
221            expect(val > 100u, "f > 100kHz");
222            expect(val < 10000000u, "f < 10GHz");
223            break;
224        case IdentifierType::RDS_PI:
225            expect(val != 0u, "RDS PI != 0");
226            expect(val <= 0xFFFFu, "16bit id");
227            break;
228        case IdentifierType::HD_STATION_ID_EXT: {
229            auto stationId = val & 0xFFFFFFFF;  // 32bit
230            val >>= 32;
231            auto subchannel = val & 0xF;  // 4bit
232            val >>= 4;
233            auto freq = val & 0x3FFFF;  // 18bit
234            expect(stationId != 0u, "HD station id != 0");
235            expect(subchannel < 8u, "HD subch < 8");
236            expect(freq > 100u, "f > 100kHz");
237            expect(freq < 10000000u, "f < 10GHz");
238            break;
239        }
240        case IdentifierType::HD_STATION_NAME: {
241            while (val > 0) {
242                auto ch = static_cast<char>(val & 0xFF);
243                val >>= 8;
244                expect((ch >= '0' && ch <= '9') || (ch >= 'A' && ch <= 'Z'),
245                       "HD_STATION_NAME does not match [A-Z0-9]+");
246            }
247            break;
248        }
249        case IdentifierType::DAB_SID_EXT: {
250            auto sid = val & 0xFFFF;  // 16bit
251            val >>= 16;
252            auto ecc = val & 0xFF;  // 8bit
253            expect(sid != 0u, "DAB SId != 0");
254            expect(ecc >= 0xA0u && ecc <= 0xF6u, "Invalid ECC, see ETSI TS 101 756 V2.1.1");
255            break;
256        }
257        case IdentifierType::DAB_ENSEMBLE:
258            expect(val != 0u, "DAB ensemble != 0");
259            expect(val <= 0xFFFFu, "16bit id");
260            break;
261        case IdentifierType::DAB_SCID:
262            expect(val > 0xFu, "12bit SCId (not 4bit SCIdS)");
263            expect(val <= 0xFFFu, "12bit id");
264            break;
265        case IdentifierType::DRMO_SERVICE_ID:
266            expect(val != 0u, "DRM SId != 0");
267            expect(val <= 0xFFFFFFu, "24bit id");
268            break;
269        case IdentifierType::SXM_SERVICE_ID:
270            expect(val != 0u, "SXM SId != 0");
271            expect(val <= 0xFFFFFFFFu, "32bit id");
272            break;
273        case IdentifierType::SXM_CHANNEL:
274            expect(val < 1000u, "SXM channel < 1000");
275            break;
276        case IdentifierType::VENDOR_START:
277        case IdentifierType::VENDOR_END:
278            // skip
279            break;
280    }
281
282    return valid;
283}
284
285bool isValid(const ProgramSelector& sel) {
286    if (!isValid(sel.primaryId)) return false;
287    // TODO(twasilczyk): use IdentifierIterator
288    for (auto&& id : sel.secondaryIds) {
289        if (!isValid(id)) return false;
290    }
291    return true;
292}
293
294ProgramIdentifier make_identifier(IdentifierType type, uint64_t value) {
295    return {static_cast<uint32_t>(type), value};
296}
297
298ProgramSelector make_selector_amfm(uint32_t frequency) {
299    ProgramSelector sel = {};
300    sel.primaryId = make_identifier(IdentifierType::AMFM_FREQUENCY, frequency);
301    return sel;
302}
303
304Metadata make_metadata(MetadataKey key, int64_t value) {
305    Metadata meta = {};
306    meta.key = static_cast<uint32_t>(key);
307    meta.intValue = value;
308    return meta;
309}
310
311Metadata make_metadata(MetadataKey key, string value) {
312    Metadata meta = {};
313    meta.key = static_cast<uint32_t>(key);
314    meta.stringValue = value;
315    return meta;
316}
317
318bool satisfies(const ProgramFilter& filter, const ProgramSelector& sel) {
319    if (filter.identifierTypes.size() > 0) {
320        auto typeEquals = [](const V2_0::ProgramIdentifier& id, uint32_t type) {
321            return id.type == type;
322        };
323        auto it = std::find_first_of(begin(sel), end(sel), filter.identifierTypes.begin(),
324                                     filter.identifierTypes.end(), typeEquals);
325        if (it == end(sel)) return false;
326    }
327
328    if (filter.identifiers.size() > 0) {
329        auto it = std::find_first_of(begin(sel), end(sel), filter.identifiers.begin(),
330                                     filter.identifiers.end());
331        if (it == end(sel)) return false;
332    }
333
334    if (!filter.includeCategories) {
335        if (getType(sel.primaryId) == IdentifierType::DAB_ENSEMBLE) return false;
336    }
337
338    return true;
339}
340
341size_t ProgramInfoHasher::operator()(const ProgramInfo& info) const {
342    auto& id = info.selector.primaryId;
343
344    /* This is not the best hash implementation, but good enough for default HAL
345     * implementation and tests. */
346    auto h = std::hash<uint32_t>{}(id.type);
347    h += 0x9e3779b9;
348    h ^= std::hash<uint64_t>{}(id.value);
349
350    return h;
351}
352
353bool ProgramInfoKeyEqual::operator()(const ProgramInfo& info1, const ProgramInfo& info2) const {
354    auto& id1 = info1.selector.primaryId;
355    auto& id2 = info2.selector.primaryId;
356    return id1.type == id2.type && id1.value == id2.value;
357}
358
359void updateProgramList(ProgramInfoSet& list, const ProgramListChunk& chunk) {
360    if (chunk.purge) list.clear();
361
362    list.insert(chunk.modified.begin(), chunk.modified.end());
363
364    for (auto&& id : chunk.removed) {
365        ProgramInfo info = {};
366        info.selector.primaryId = id;
367        list.erase(info);
368    }
369}
370
371std::optional<std::string> getMetadataString(const V2_0::ProgramInfo& info,
372                                             const V2_0::MetadataKey key) {
373    auto isKey = [key](const V2_0::Metadata& item) {
374        return static_cast<V2_0::MetadataKey>(item.key) == key;
375    };
376
377    auto it = std::find_if(info.metadata.begin(), info.metadata.end(), isKey);
378    if (it == info.metadata.end()) return std::nullopt;
379
380    return it->stringValue;
381}
382
383V2_0::ProgramIdentifier make_hdradio_station_name(const std::string& name) {
384    constexpr size_t maxlen = 8;
385
386    std::string shortName;
387    shortName.reserve(maxlen);
388
389    auto&& loc = std::locale::classic();
390    for (char ch : name) {
391        if (!std::isalnum(ch, loc)) continue;
392        shortName.push_back(std::toupper(ch, loc));
393        if (shortName.length() >= maxlen) break;
394    }
395
396    uint64_t val = 0;
397    for (auto rit = shortName.rbegin(); rit != shortName.rend(); ++rit) {
398        val <<= 8;
399        val |= static_cast<uint8_t>(*rit);
400    }
401
402    return make_identifier(IdentifierType::HD_STATION_NAME, val);
403}
404
405}  // namespace utils
406
407namespace V2_0 {
408
409utils::IdentifierIterator begin(const ProgramSelector& sel) {
410    return utils::IdentifierIterator(sel);
411}
412
413utils::IdentifierIterator end(const ProgramSelector& sel) {
414    return utils::IdentifierIterator(sel) + 1 /* primary id */ + sel.secondaryIds.size();
415}
416
417}  // namespace V2_0
418}  // namespace broadcastradio
419}  // namespace hardware
420}  // namespace android
421