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.
250 lines
7.9 KiB
250 lines
7.9 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 "BroadcastRadioDefault.utils"
|
|
//#define LOG_NDEBUG 0
|
|
|
|
#include <broadcastradio-utils-1x/Utils.h>
|
|
|
|
#include <log/log.h>
|
|
|
|
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<ProgramType>(ia);
|
|
auto b = static_cast<ProgramType>(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<ProgramType>(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<uint32_t>(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<uint32_t>(type);
|
|
sel.primaryId.type = static_cast<uint32_t>(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<ProgramIdentifier>{
|
|
{static_cast<uint32_t>(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
|