/* * 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 "BroadcastRadioDefault.utils" //#define LOG_NDEBUG 0 #include #include namespace android { namespace hardware { namespace broadcastradio { namespace utils { using V1_0::Band; using V1_1::IdentifierType; using V1_1::ProgramIdentifier; using V1_1::ProgramSelector; using V1_1::ProgramType; static bool isCompatibleProgramType(const uint32_t ia, const uint32_t ib) { auto a = static_cast(ia); auto b = static_cast(ib); if (a == b) return true; if (a == ProgramType::AM && b == ProgramType::AM_HD) return true; if (a == ProgramType::AM_HD && b == ProgramType::AM) return true; if (a == ProgramType::FM && b == ProgramType::FM_HD) return true; if (a == ProgramType::FM_HD && b == ProgramType::FM) return true; return false; } static bool bothHaveId(const ProgramSelector& a, const ProgramSelector& b, const IdentifierType type) { return hasId(a, type) && hasId(b, type); } static bool anyHaveId(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); } bool tunesTo(const ProgramSelector& a, const ProgramSelector& b) { if (!isCompatibleProgramType(a.programType, b.programType)) return false; auto type = getType(a); switch (type) { case ProgramType::AM: case ProgramType::AM_HD: case ProgramType::FM: case ProgramType::FM_HD: if (haveEqualIds(a, b, IdentifierType::HD_STATION_ID_EXT)) return true; // if HD Radio subchannel is specified, it must match if (anyHaveId(a, b, IdentifierType::HD_SUBCHANNEL)) { // missing subchannel (analog) is an equivalent of first subchannel (MPS) auto aCh = getId(a, IdentifierType::HD_SUBCHANNEL, 0); auto bCh = getId(b, IdentifierType::HD_SUBCHANNEL, 0); if (aCh != bCh) return false; } if (haveEqualIds(a, b, IdentifierType::RDS_PI)) return true; return haveEqualIds(a, b, IdentifierType::AMFM_FREQUENCY); case ProgramType::DAB: return haveEqualIds(a, b, IdentifierType::DAB_SIDECC); case ProgramType::DRMO: return haveEqualIds(a, b, IdentifierType::DRMO_SERVICE_ID); case ProgramType::SXM: if (anyHaveId(a, b, IdentifierType::SXM_SERVICE_ID)) { return haveEqualIds(a, b, IdentifierType::SXM_SERVICE_ID); } return haveEqualIds(a, b, IdentifierType::SXM_CHANNEL); default: // includes all vendor types ALOGW("Unsupported program type: %s", toString(type).c_str()); return false; } } ProgramType getType(const ProgramSelector& sel) { return static_cast(sel.programType); } bool isAmFm(const ProgramType type) { switch (type) { case ProgramType::AM: case ProgramType::FM: case ProgramType::AM_HD: case ProgramType::FM_HD: return true; default: return false; } } bool isAm(const Band band) { return band == Band::AM || band == Band::AM_HD; } bool isFm(const Band band) { return band == Band::FM || band == Band::FM_HD; } static bool maybeGetId(const ProgramSelector& sel, const IdentifierType type, uint64_t* val) { auto itype = static_cast(type); if (sel.primaryId.type == itype) { if (val) *val = sel.primaryId.value; return true; } // 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; } ALOGW("Identifier %s not found", toString(type).c_str()); return 0; } uint64_t getId(const ProgramSelector& sel, const IdentifierType type, uint64_t defval) { if (!hasId(sel, type)) return defval; return getId(sel, type); } ProgramSelector make_selector(Band band, uint32_t channel, uint32_t subChannel) { ProgramSelector sel = {}; ALOGW_IF((subChannel > 0) && (band == Band::AM || band == Band::FM), "got subChannel for non-HD AM/FM"); // we can't use ProgramType::AM_HD or FM_HD, because we don't know HD station ID ProgramType type; if (isAm(band)) { type = ProgramType::AM; } else if (isFm(band)) { type = ProgramType::FM; } else { LOG_ALWAYS_FATAL("Unsupported band: %s", toString(band).c_str()); } sel.programType = static_cast(type); sel.primaryId.type = static_cast(IdentifierType::AMFM_FREQUENCY); sel.primaryId.value = channel; if (subChannel > 0) { /* stating sub channel for AM/FM channel does not give any guarantees, * but we can't do much more without HD station ID * * The legacy APIs had 1-based subChannels, while ProgramSelector is 0-based. */ sel.secondaryIds = hidl_vec{ {static_cast(IdentifierType::HD_SUBCHANNEL), subChannel - 1}, }; } return sel; } bool getLegacyChannel(const ProgramSelector& sel, uint32_t* channelOut, uint32_t* subChannelOut) { if (channelOut) *channelOut = 0; if (subChannelOut) *subChannelOut = 0; if (isAmFm(getType(sel))) { if (channelOut) *channelOut = getId(sel, IdentifierType::AMFM_FREQUENCY); if (subChannelOut && hasId(sel, IdentifierType::HD_SUBCHANNEL)) { // The legacy APIs had 1-based subChannels, while ProgramSelector is 0-based. *subChannelOut = getId(sel, IdentifierType::HD_SUBCHANNEL) + 1; } return true; } return false; } bool isDigital(const ProgramSelector& sel) { switch (getType(sel)) { case ProgramType::AM: case ProgramType::FM: return false; default: // VENDOR might not be digital, but it doesn't matter for default impl. return true; } } } // namespace utils namespace V1_0 { bool operator==(const BandConfig& l, const BandConfig& r) { using namespace utils; if (l.type != r.type) return false; if (l.antennaConnected != r.antennaConnected) return false; if (l.lowerLimit != r.lowerLimit) return false; if (l.upperLimit != r.upperLimit) return false; if (l.spacings != r.spacings) return false; if (isAm(l.type)) { return l.ext.am == r.ext.am; } else if (isFm(l.type)) { return l.ext.fm == r.ext.fm; } else { ALOGW("Unsupported band config type: %s", toString(l.type).c_str()); return false; } } } // namespace V1_0 } // namespace broadcastradio } // namespace hardware } // namespace android