Files
hardware_interfaces/broadcastradio/common/utilsaidl/Utils.cpp
Weilin Xu 64cb963141 Allow secondary ids missing for DAB selector
Sometimes DAB program selector used in tuning does not have secondary
identifiers such as ensemble and frequency, and HAL will automatically
selector a DAB station with specified ensemble and frequency. Thus,
secondary ids in DAB program selector are not mandatory in HAL. Also
updated broadcast radio AIDL HAL documentation.

Bug: 273804622
Test: atest VtsHalBroadcastradioAidlTargetTest
Change-Id: If05b7eeb79e299667c3a64bf8b931bb6396f9045
2023-03-22 18:18:31 +00:00

559 lines
18 KiB
C++

/*
* Copyright (C) 2022 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 "BcRadioAidlDef.utils"
#include "broadcastradio-utils-aidl/Utils.h"
#include <android-base/logging.h>
#include <android-base/parseint.h>
#include <android-base/strings.h>
#include <math/HashCombine.h>
namespace aidl::android::hardware::broadcastradio {
namespace utils {
namespace {
using ::android::base::EqualsIgnoreCase;
using ::std::string;
using ::std::vector;
const int64_t kValueForNotFoundIdentifier = 0;
bool bothHaveId(const ProgramSelector& a, const ProgramSelector& b, const IdentifierType& type) {
return hasId(a, type) && hasId(b, type);
}
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);
}
int getHdSubchannel(const ProgramSelector& sel) {
int64_t hdSidExt = getId(sel, IdentifierType::HD_STATION_ID_EXT, /* defaultValue */ 0);
hdSidExt >>= 32; // Station ID number
return hdSidExt & 0xF; // HD Radio subchannel
}
bool maybeGetId(const ProgramSelector& sel, const IdentifierType& type, int64_t* val) {
// iterate through primaryId and secondaryIds
for (auto it = begin(sel); it != end(sel); it++) {
if (it->type == type) {
if (val != nullptr) {
*val = it->value;
}
return true;
}
}
return false;
}
} // namespace
IdentifierIterator::IdentifierIterator(const ProgramSelector& sel) : IdentifierIterator(sel, 0) {}
IdentifierIterator::IdentifierIterator(const ProgramSelector& sel, size_t pos)
: mSel(sel), mPos(pos) {}
const IdentifierIterator IdentifierIterator::operator++(int) {
IdentifierIterator i = *this;
mPos++;
return i;
}
IdentifierIterator& IdentifierIterator::operator++() {
++mPos;
return *this;
}
IdentifierIterator::refType IdentifierIterator::operator*() const {
if (mPos == 0) {
return getSelector().primaryId;
}
// mPos is 1-based for secondary identifiers
DCHECK(mPos <= getSelector().secondaryIds.size());
return getSelector().secondaryIds[mPos - 1];
}
bool IdentifierIterator::operator==(const IdentifierIterator& rhs) const {
// Check, if both iterators points at the same selector.
if (reinterpret_cast<intptr_t>(&getSelector()) !=
reinterpret_cast<intptr_t>(&rhs.getSelector())) {
return false;
}
return mPos == rhs.mPos;
}
int32_t resultToInt(Result result) {
return static_cast<int32_t>(result);
}
FrequencyBand getBand(int64_t freq) {
// keep in sync with
// frameworks/base/services/core/java/com/android/server/broadcastradio/aidl/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;
}
bool tunesTo(const ProgramSelector& a, const ProgramSelector& b) {
IdentifierType type = b.primaryId.type;
switch (type) {
case IdentifierType::HD_STATION_ID_EXT:
case IdentifierType::RDS_PI:
case IdentifierType::AMFM_FREQUENCY_KHZ:
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_KHZ);
case IdentifierType::DAB_SID_EXT:
if (!haveEqualIds(a, b, IdentifierType::DAB_SID_EXT)) {
return false;
}
if (hasId(a, IdentifierType::DAB_ENSEMBLE) &&
!haveEqualIds(a, b, IdentifierType::DAB_ENSEMBLE)) {
return false;
}
if (hasId(a, IdentifierType::DAB_FREQUENCY_KHZ) &&
!haveEqualIds(a, b, IdentifierType::DAB_FREQUENCY_KHZ)) {
return false;
}
return true;
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;
}
}
bool hasId(const ProgramSelector& sel, const IdentifierType& type) {
return maybeGetId(sel, type, /* val */ nullptr);
}
int64_t getId(const ProgramSelector& sel, const IdentifierType& type) {
int64_t val;
if (maybeGetId(sel, type, &val)) {
return val;
}
LOG(WARNING) << "identifier not found: " << toString(type);
return kValueForNotFoundIdentifier;
}
int64_t getId(const ProgramSelector& sel, const IdentifierType& type, int64_t defaultValue) {
if (!hasId(sel, type)) {
return defaultValue;
}
return getId(sel, type);
}
vector<int> getAllIds(const ProgramSelector& sel, const IdentifierType& type) {
vector<int> ret;
// iterate through primaryId and secondaryIds
for (auto it = begin(sel); it != end(sel); it++) {
if (it->type == type) {
ret.push_back(it->value);
}
}
return ret;
}
bool isSupported(const Properties& prop, const ProgramSelector& sel) {
for (auto it = prop.supportedIdentifierTypes.begin(); it != prop.supportedIdentifierTypes.end();
it++) {
if (hasId(sel, *it)) {
return true;
}
}
return false;
}
bool isValid(const ProgramIdentifier& id) {
int64_t val = id.value;
bool valid = true;
auto expect = [&valid](bool condition, const string& message) {
if (!condition) {
valid = false;
LOG(ERROR) << "identifier not valid, expected " << message;
}
};
switch (id.type) {
case IdentifierType::INVALID:
expect(false, "IdentifierType::INVALID");
break;
case IdentifierType::DAB_FREQUENCY_KHZ:
expect(val > 100000u, "f > 100MHz");
[[fallthrough]];
case IdentifierType::AMFM_FREQUENCY_KHZ:
case IdentifierType::DRMO_FREQUENCY_KHZ:
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: {
int64_t stationId = val & 0xFFFFFFFF; // 32bit
val >>= 32;
int64_t subchannel = val & 0xF; // 4bit
val >>= 4;
int64_t 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) {
char 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: {
int64_t sid = val & 0xFFFFFFFF; // 32bit
val >>= 32;
int64_t 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 (sel.primaryId.type != IdentifierType::AMFM_FREQUENCY_KHZ &&
sel.primaryId.type != IdentifierType::RDS_PI &&
sel.primaryId.type != IdentifierType::HD_STATION_ID_EXT &&
sel.primaryId.type != IdentifierType::DAB_SID_EXT &&
sel.primaryId.type != IdentifierType::DRMO_SERVICE_ID &&
sel.primaryId.type != IdentifierType::SXM_SERVICE_ID &&
(sel.primaryId.type < IdentifierType::VENDOR_START ||
sel.primaryId.type > IdentifierType::VENDOR_END)) {
return false;
}
return isValid(sel.primaryId);
}
ProgramIdentifier makeIdentifier(IdentifierType type, int64_t value) {
return {type, value};
}
ProgramSelector makeSelectorAmfm(int32_t frequency) {
ProgramSelector sel = {};
sel.primaryId = makeIdentifier(IdentifierType::AMFM_FREQUENCY_KHZ, frequency);
return sel;
}
ProgramSelector makeSelectorDab(int64_t sidExt) {
ProgramSelector sel = {};
sel.primaryId = makeIdentifier(IdentifierType::DAB_SID_EXT, sidExt);
return sel;
}
ProgramSelector makeSelectorDab(int64_t sidExt, int32_t ensemble, int64_t freq) {
ProgramSelector sel = {};
sel.primaryId = makeIdentifier(IdentifierType::DAB_SID_EXT, sidExt);
vector<ProgramIdentifier> secondaryIds = {
makeIdentifier(IdentifierType::DAB_ENSEMBLE, ensemble),
makeIdentifier(IdentifierType::DAB_FREQUENCY_KHZ, freq)};
sel.secondaryIds = std::move(secondaryIds);
return sel;
}
bool satisfies(const ProgramFilter& filter, const ProgramSelector& sel) {
if (filter.identifierTypes.size() > 0) {
auto typeEquals = [](const ProgramIdentifier& id, IdentifierType 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;
}
}
return true;
}
size_t ProgramInfoHasher::operator()(const ProgramInfo& info) const {
const ProgramIdentifier& id = info.selector.primaryId;
// This is not the best hash implementation, but good enough for default HAL
// implementation and tests.
size_t h = 0;
::android::hashCombineSingle(h, id.type);
::android::hashCombineSingle(h, id.value);
return h;
}
bool ProgramInfoKeyEqual::operator()(const ProgramInfo& info1, const ProgramInfo& info2) const {
const ProgramIdentifier& id1 = info1.selector.primaryId;
const ProgramIdentifier& id2 = info2.selector.primaryId;
return id1.type == id2.type && id1.value == id2.value;
}
void updateProgramList(const ProgramListChunk& chunk, ProgramInfoSet* list) {
if (chunk.purge) {
list->clear();
}
list->insert(chunk.modified.begin(), chunk.modified.end());
if (!chunk.removed.has_value()) {
return;
}
for (auto& id : chunk.removed.value()) {
if (id.has_value()) {
ProgramInfo info = {};
info.selector.primaryId = id.value();
list->erase(info);
}
}
}
std::optional<std::string> getMetadataString(const ProgramInfo& info, const Metadata::Tag& tag) {
auto isRdsPs = [tag](const Metadata& item) { return item.getTag() == tag; };
auto it = std::find_if(info.metadata.begin(), info.metadata.end(), isRdsPs);
if (it == info.metadata.end()) {
return std::nullopt;
}
std::string metadataString;
switch (it->getTag()) {
case Metadata::rdsPs:
metadataString = it->get<Metadata::rdsPs>();
break;
case Metadata::rdsPty:
metadataString = std::to_string(it->get<Metadata::rdsPty>());
break;
case Metadata::rbdsPty:
metadataString = std::to_string(it->get<Metadata::rbdsPty>());
break;
case Metadata::rdsRt:
metadataString = it->get<Metadata::rdsRt>();
break;
case Metadata::songTitle:
metadataString = it->get<Metadata::songTitle>();
break;
case Metadata::songArtist:
metadataString = it->get<Metadata::songArtist>();
break;
case Metadata::songAlbum:
metadataString = it->get<Metadata::songAlbum>();
break;
case Metadata::stationIcon:
metadataString = std::to_string(it->get<Metadata::stationIcon>());
break;
case Metadata::albumArt:
metadataString = std::to_string(it->get<Metadata::albumArt>());
break;
case Metadata::programName:
metadataString = it->get<Metadata::programName>();
break;
case Metadata::dabEnsembleName:
metadataString = it->get<Metadata::dabEnsembleName>();
break;
case Metadata::dabEnsembleNameShort:
metadataString = it->get<Metadata::dabEnsembleNameShort>();
break;
case Metadata::dabServiceName:
metadataString = it->get<Metadata::dabServiceName>();
break;
case Metadata::dabServiceNameShort:
metadataString = it->get<Metadata::dabServiceNameShort>();
break;
case Metadata::dabComponentName:
metadataString = it->get<Metadata::dabComponentName>();
break;
case Metadata::dabComponentNameShort:
metadataString = it->get<Metadata::dabComponentNameShort>();
break;
default:
LOG(ERROR) << "Metadata " << it->toString() << " is not converted.";
return std::nullopt;
}
return metadataString;
}
ProgramIdentifier makeHdRadioStationName(const string& name) {
constexpr size_t maxlen = 8;
string shortName;
shortName.reserve(maxlen);
const auto& loc = std::locale::classic();
for (const char& ch : name) {
if (!std::isalnum(ch, loc)) {
continue;
}
shortName.push_back(std::toupper(ch, loc));
if (shortName.length() >= maxlen) {
break;
}
}
// Short name is converted to HD_STATION_NAME by encoding each char into its ASCII value in
// in little-endian order. For example, "Abc" is converted to 0x434241.
int64_t val = 0;
for (auto rit = shortName.rbegin(); rit != shortName.rend(); ++rit) {
val <<= 8;
val |= static_cast<char>(*rit);
}
return makeIdentifier(IdentifierType::HD_STATION_NAME, val);
}
IdentifierType getType(int typeAsInt) {
return static_cast<IdentifierType>(typeAsInt);
}
bool parseArgInt(const string& s, int* out) {
return ::android::base::ParseInt(s, out);
}
bool parseArgLong(const std::string& s, long* out) {
return ::android::base::ParseInt(s, out);
}
bool parseArgBool(const string& s, bool* out) {
if (EqualsIgnoreCase(s, "true")) {
*out = true;
} else if (EqualsIgnoreCase(s, "false")) {
*out = false;
} else {
return false;
}
return true;
}
bool parseArgDirection(const string& s, bool* out) {
if (EqualsIgnoreCase(s, "up")) {
*out = true;
} else if (EqualsIgnoreCase(s, "down")) {
*out = false;
} else {
return false;
}
return true;
}
bool parseArgIdentifierTypeArray(const string& s, vector<IdentifierType>* out) {
for (const string& val : ::android::base::Split(s, ",")) {
int outInt;
if (!parseArgInt(val, &outInt)) {
return false;
}
out->push_back(getType(outInt));
}
return true;
}
bool parseProgramIdentifierList(const std::string& s, vector<ProgramIdentifier>* out) {
for (const string& idStr : ::android::base::Split(s, ",")) {
const vector<string> idStrPair = ::android::base::Split(idStr, ":");
if (idStrPair.size() != 2) {
return false;
}
int idType;
if (!parseArgInt(idStrPair[0], &idType)) {
return false;
}
long idVal;
if (!parseArgLong(idStrPair[1], &idVal)) {
return false;
}
ProgramIdentifier id = {getType(idType), idVal};
out->push_back(id);
}
return true;
}
} // namespace utils
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 aidl::android::hardware::broadcastradio