You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

433 lines
14 KiB

/*
* Copyright (C) 2017 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#define LOG_TAG "BcRadioDef.utils"
#include <broadcastradio-utils-2x/Utils.h>
#include <android-base/logging.h>
namespace android {
namespace hardware {
namespace broadcastradio {
namespace utils {
using V2_0::IdentifierType;
using V2_0::Metadata;
using V2_0::MetadataKey;
using V2_0::ProgramFilter;
using V2_0::ProgramIdentifier;
using V2_0::ProgramInfo;
using V2_0::ProgramListChunk;
using V2_0::ProgramSelector;
using V2_0::Properties;
using std::string;
using std::vector;
IdentifierType getType(uint32_t typeAsInt) {
return static_cast<IdentifierType>(typeAsInt);
}
IdentifierType getType(const ProgramIdentifier& id) {
return getType(id.type);
}
IdentifierIterator::IdentifierIterator(const V2_0::ProgramSelector& sel)
: IdentifierIterator(sel, 0) {}
IdentifierIterator::IdentifierIterator(const V2_0::ProgramSelector& sel, size_t pos)
: mSel(sel), mPos(pos) {}
IdentifierIterator IdentifierIterator::operator++(int) {
auto i = *this;
mPos++;
return i;
}
IdentifierIterator& IdentifierIterator::operator++() {
++mPos;
return *this;
}
IdentifierIterator::ref_type IdentifierIterator::operator*() const {
if (mPos == 0) return sel().primaryId;
// mPos is 1-based for secondary identifiers
DCHECK(mPos <= sel().secondaryIds.size());
return sel().secondaryIds[mPos - 1];
}
bool IdentifierIterator::operator==(const IdentifierIterator& rhs) const {
// Check, if both iterators points at the same selector.
if (reinterpret_cast<uintptr_t>(&sel()) != reinterpret_cast<uintptr_t>(&rhs.sel())) {
return false;
}
return mPos == rhs.mPos;
}
FrequencyBand getBand(uint64_t freq) {
// keep in sync with
// frameworks/base/services/core/java/com/android/server/broadcastradio/hal2/Utils.java
if (freq < 30) return FrequencyBand::UNKNOWN;
if (freq < 500) return FrequencyBand::AM_LW;
if (freq < 1705) return FrequencyBand::AM_MW;
if (freq < 30000) return FrequencyBand::AM_SW;
if (freq < 60000) return FrequencyBand::UNKNOWN;
if (freq < 110000) return FrequencyBand::FM;
return FrequencyBand::UNKNOWN;
}
static bool bothHaveId(const ProgramSelector& a, const ProgramSelector& b,
const IdentifierType type) {
return hasId(a, type) && hasId(b, type);
}
static bool haveEqualIds(const ProgramSelector& a, const ProgramSelector& b,
const IdentifierType type) {
if (!bothHaveId(a, b, type)) return false;
/* We should check all Ids of a given type (ie. other AF),
* but it doesn't matter for default implementation.
*/
return getId(a, type) == getId(b, type);
}
static int getHdSubchannel(const ProgramSelector& sel) {
auto hdsidext = getId(sel, IdentifierType::HD_STATION_ID_EXT, 0);
hdsidext >>= 32; // Station ID number
return hdsidext & 0xF; // HD Radio subchannel
}
bool tunesTo(const ProgramSelector& a, const ProgramSelector& b) {
auto type = getType(b.primaryId);
switch (type) {
case IdentifierType::HD_STATION_ID_EXT:
case IdentifierType::RDS_PI:
case IdentifierType::AMFM_FREQUENCY:
if (haveEqualIds(a, b, IdentifierType::HD_STATION_ID_EXT)) return true;
if (haveEqualIds(a, b, IdentifierType::RDS_PI)) return true;
return getHdSubchannel(b) == 0 && haveEqualIds(a, b, IdentifierType::AMFM_FREQUENCY);
case IdentifierType::DAB_SID_EXT:
return haveEqualIds(a, b, IdentifierType::DAB_SID_EXT);
case IdentifierType::DRMO_SERVICE_ID:
return haveEqualIds(a, b, IdentifierType::DRMO_SERVICE_ID);
case IdentifierType::SXM_SERVICE_ID:
return haveEqualIds(a, b, IdentifierType::SXM_SERVICE_ID);
default: // includes all vendor types
LOG(WARNING) << "unsupported program type: " << toString(type);
return false;
}
}
static bool maybeGetId(const ProgramSelector& sel, const IdentifierType type, uint64_t* val) {
auto itype = static_cast<uint32_t>(type);
if (sel.primaryId.type == itype) {
if (val) *val = sel.primaryId.value;
return true;
}
// TODO(twasilczyk): use IdentifierIterator
// not optimal, but we don't care in default impl
for (auto&& id : sel.secondaryIds) {
if (id.type == itype) {
if (val) *val = id.value;
return true;
}
}
return false;
}
bool hasId(const ProgramSelector& sel, const IdentifierType type) {
return maybeGetId(sel, type, nullptr);
}
uint64_t getId(const ProgramSelector& sel, const IdentifierType type) {
uint64_t val;
if (maybeGetId(sel, type, &val)) {
return val;
}
LOG(WARNING) << "identifier not found: " << toString(type);
return 0;
}
uint64_t getId(const ProgramSelector& sel, const IdentifierType type, uint64_t defval) {
if (!hasId(sel, type)) return defval;
return getId(sel, type);
}
vector<uint64_t> getAllIds(const ProgramSelector& sel, const IdentifierType type) {
vector<uint64_t> ret;
auto itype = static_cast<uint32_t>(type);
if (sel.primaryId.type == itype) ret.push_back(sel.primaryId.value);
// TODO(twasilczyk): use IdentifierIterator
for (auto&& id : sel.secondaryIds) {
if (id.type == itype) ret.push_back(id.value);
}
return ret;
}
bool isSupported(const Properties& prop, const ProgramSelector& sel) {
// TODO(twasilczyk): use IdentifierIterator
// Not optimal, but it doesn't matter for default impl nor VTS tests.
for (auto&& idType : prop.supportedIdentifierTypes) {
if (hasId(sel, getType(idType))) return true;
}
return false;
}
bool isValid(const ProgramIdentifier& id) {
auto val = id.value;
bool valid = true;
auto expect = [&valid](bool condition, std::string message) {
if (!condition) {
valid = false;
LOG(ERROR) << "identifier not valid, expected " << message;
}
};
switch (getType(id)) {
case IdentifierType::INVALID:
expect(false, "IdentifierType::INVALID");
break;
case IdentifierType::DAB_FREQUENCY:
expect(val > 100000u, "f > 100MHz");
[[fallthrough]];
case IdentifierType::AMFM_FREQUENCY:
case IdentifierType::DRMO_FREQUENCY:
expect(val > 100u, "f > 100kHz");
expect(val < 10000000u, "f < 10GHz");
break;
case IdentifierType::RDS_PI:
expect(val != 0u, "RDS PI != 0");
expect(val <= 0xFFFFu, "16bit id");
break;
case IdentifierType::HD_STATION_ID_EXT: {
auto stationId = val & 0xFFFFFFFF; // 32bit
val >>= 32;
auto subchannel = val & 0xF; // 4bit
val >>= 4;
auto freq = val & 0x3FFFF; // 18bit
expect(stationId != 0u, "HD station id != 0");
expect(subchannel < 8u, "HD subch < 8");
expect(freq > 100u, "f > 100kHz");
expect(freq < 10000000u, "f < 10GHz");
break;
}
case IdentifierType::HD_STATION_NAME: {
while (val > 0) {
auto ch = static_cast<char>(val & 0xFF);
val >>= 8;
expect((ch >= '0' && ch <= '9') || (ch >= 'A' && ch <= 'Z'),
"HD_STATION_NAME does not match [A-Z0-9]+");
}
break;
}
case IdentifierType::DAB_SID_EXT: {
auto sid = val & 0xFFFF; // 16bit
val >>= 16;
auto ecc = val & 0xFF; // 8bit
expect(sid != 0u, "DAB SId != 0");
expect(ecc >= 0xA0u && ecc <= 0xF6u, "Invalid ECC, see ETSI TS 101 756 V2.1.1");
break;
}
case IdentifierType::DAB_ENSEMBLE:
expect(val != 0u, "DAB ensemble != 0");
expect(val <= 0xFFFFu, "16bit id");
break;
case IdentifierType::DAB_SCID:
expect(val > 0xFu, "12bit SCId (not 4bit SCIdS)");
expect(val <= 0xFFFu, "12bit id");
break;
case IdentifierType::DRMO_SERVICE_ID:
expect(val != 0u, "DRM SId != 0");
expect(val <= 0xFFFFFFu, "24bit id");
break;
case IdentifierType::SXM_SERVICE_ID:
expect(val != 0u, "SXM SId != 0");
expect(val <= 0xFFFFFFFFu, "32bit id");
break;
case IdentifierType::SXM_CHANNEL:
expect(val < 1000u, "SXM channel < 1000");
break;
case IdentifierType::VENDOR_START:
case IdentifierType::VENDOR_END:
// skip
break;
}
return valid;
}
bool isValid(const ProgramSelector& sel) {
if (!isValid(sel.primaryId)) return false;
// TODO(twasilczyk): use IdentifierIterator
for (auto&& id : sel.secondaryIds) {
if (!isValid(id)) return false;
}
return true;
}
ProgramIdentifier make_identifier(IdentifierType type, uint64_t value) {
return {static_cast<uint32_t>(type), value};
}
ProgramSelector make_selector_amfm(uint32_t frequency) {
ProgramSelector sel = {};
sel.primaryId = make_identifier(IdentifierType::AMFM_FREQUENCY, frequency);
return sel;
}
ProgramSelector make_selector_dab(uint32_t sidExt, uint32_t ensemble) {
ProgramSelector sel = {};
// TODO(maryabad): Have a helper function to create the sidExt instead of
// passing the whole identifier here. Something like make_dab_sid_ext.
sel.primaryId = make_identifier(IdentifierType::DAB_SID_EXT, sidExt);
hidl_vec<ProgramIdentifier> secondaryIds = {
make_identifier(IdentifierType::DAB_ENSEMBLE, ensemble),
// TODO(maryabad): Include frequency here when the helper method to
// translate between ensemble and frequency is implemented.
};
sel.secondaryIds = secondaryIds;
return sel;
}
Metadata make_metadata(MetadataKey key, int64_t value) {
Metadata meta = {};
meta.key = static_cast<uint32_t>(key);
meta.intValue = value;
return meta;
}
Metadata make_metadata(MetadataKey key, string value) {
Metadata meta = {};
meta.key = static_cast<uint32_t>(key);
meta.stringValue = value;
return meta;
}
bool satisfies(const ProgramFilter& filter, const ProgramSelector& sel) {
if (filter.identifierTypes.size() > 0) {
auto typeEquals = [](const V2_0::ProgramIdentifier& id, uint32_t type) {
return id.type == type;
};
auto it = std::find_first_of(begin(sel), end(sel), filter.identifierTypes.begin(),
filter.identifierTypes.end(), typeEquals);
if (it == end(sel)) return false;
}
if (filter.identifiers.size() > 0) {
auto it = std::find_first_of(begin(sel), end(sel), filter.identifiers.begin(),
filter.identifiers.end());
if (it == end(sel)) return false;
}
if (!filter.includeCategories) {
if (getType(sel.primaryId) == IdentifierType::DAB_ENSEMBLE) return false;
}
return true;
}
size_t ProgramInfoHasher::operator()(const ProgramInfo& info) const {
auto& id = info.selector.primaryId;
/* This is not the best hash implementation, but good enough for default HAL
* implementation and tests. */
auto h = std::hash<uint32_t>{}(id.type);
h += 0x9e3779b9;
h ^= std::hash<uint64_t>{}(id.value);
return h;
}
bool ProgramInfoKeyEqual::operator()(const ProgramInfo& info1, const ProgramInfo& info2) const {
auto& id1 = info1.selector.primaryId;
auto& id2 = info2.selector.primaryId;
return id1.type == id2.type && id1.value == id2.value;
}
void updateProgramList(ProgramInfoSet& list, const ProgramListChunk& chunk) {
if (chunk.purge) list.clear();
list.insert(chunk.modified.begin(), chunk.modified.end());
for (auto&& id : chunk.removed) {
ProgramInfo info = {};
info.selector.primaryId = id;
list.erase(info);
}
}
std::optional<std::string> getMetadataString(const V2_0::ProgramInfo& info,
const V2_0::MetadataKey key) {
auto isKey = [key](const V2_0::Metadata& item) {
return static_cast<V2_0::MetadataKey>(item.key) == key;
};
auto it = std::find_if(info.metadata.begin(), info.metadata.end(), isKey);
if (it == info.metadata.end()) return std::nullopt;
return it->stringValue;
}
V2_0::ProgramIdentifier make_hdradio_station_name(const std::string& name) {
constexpr size_t maxlen = 8;
std::string shortName;
shortName.reserve(maxlen);
auto&& loc = std::locale::classic();
for (char ch : name) {
if (!std::isalnum(ch, loc)) continue;
shortName.push_back(std::toupper(ch, loc));
if (shortName.length() >= maxlen) break;
}
uint64_t val = 0;
for (auto rit = shortName.rbegin(); rit != shortName.rend(); ++rit) {
val <<= 8;
val |= static_cast<uint8_t>(*rit);
}
return make_identifier(IdentifierType::HD_STATION_NAME, val);
}
} // namespace utils
namespace V2_0 {
utils::IdentifierIterator begin(const ProgramSelector& sel) {
return utils::IdentifierIterator(sel);
}
utils::IdentifierIterator end(const ProgramSelector& sel) {
return utils::IdentifierIterator(sel) + 1 /* primary id */ + sel.secondaryIds.size();
}
} // namespace V2_0
} // namespace broadcastradio
} // namespace hardware
} // namespace android