1*4d7e907cSAndroid Build Coastguard Worker /*
2*4d7e907cSAndroid Build Coastguard Worker * Copyright (C) 2017 The Android Open Source Project
3*4d7e907cSAndroid Build Coastguard Worker *
4*4d7e907cSAndroid Build Coastguard Worker * Licensed under the Apache License, Version 2.0 (the "License");
5*4d7e907cSAndroid Build Coastguard Worker * you may not use this file except in compliance with the License.
6*4d7e907cSAndroid Build Coastguard Worker * You may obtain a copy of the License at
7*4d7e907cSAndroid Build Coastguard Worker *
8*4d7e907cSAndroid Build Coastguard Worker * http://www.apache.org/licenses/LICENSE-2.0
9*4d7e907cSAndroid Build Coastguard Worker *
10*4d7e907cSAndroid Build Coastguard Worker * Unless required by applicable law or agreed to in writing, software
11*4d7e907cSAndroid Build Coastguard Worker * distributed under the License is distributed on an "AS IS" BASIS,
12*4d7e907cSAndroid Build Coastguard Worker * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13*4d7e907cSAndroid Build Coastguard Worker * See the License for the specific language governing permissions and
14*4d7e907cSAndroid Build Coastguard Worker * limitations under the License.
15*4d7e907cSAndroid Build Coastguard Worker */
16*4d7e907cSAndroid Build Coastguard Worker #define LOG_TAG "BroadcastRadioDefault.utils"
17*4d7e907cSAndroid Build Coastguard Worker //#define LOG_NDEBUG 0
18*4d7e907cSAndroid Build Coastguard Worker
19*4d7e907cSAndroid Build Coastguard Worker #include <broadcastradio-utils-1x/Utils.h>
20*4d7e907cSAndroid Build Coastguard Worker
21*4d7e907cSAndroid Build Coastguard Worker #include <log/log.h>
22*4d7e907cSAndroid Build Coastguard Worker
23*4d7e907cSAndroid Build Coastguard Worker namespace android {
24*4d7e907cSAndroid Build Coastguard Worker namespace hardware {
25*4d7e907cSAndroid Build Coastguard Worker namespace broadcastradio {
26*4d7e907cSAndroid Build Coastguard Worker namespace utils {
27*4d7e907cSAndroid Build Coastguard Worker
28*4d7e907cSAndroid Build Coastguard Worker using V1_0::Band;
29*4d7e907cSAndroid Build Coastguard Worker using V1_1::IdentifierType;
30*4d7e907cSAndroid Build Coastguard Worker using V1_1::ProgramIdentifier;
31*4d7e907cSAndroid Build Coastguard Worker using V1_1::ProgramSelector;
32*4d7e907cSAndroid Build Coastguard Worker using V1_1::ProgramType;
33*4d7e907cSAndroid Build Coastguard Worker
isCompatibleProgramType(const uint32_t ia,const uint32_t ib)34*4d7e907cSAndroid Build Coastguard Worker static bool isCompatibleProgramType(const uint32_t ia, const uint32_t ib) {
35*4d7e907cSAndroid Build Coastguard Worker auto a = static_cast<ProgramType>(ia);
36*4d7e907cSAndroid Build Coastguard Worker auto b = static_cast<ProgramType>(ib);
37*4d7e907cSAndroid Build Coastguard Worker
38*4d7e907cSAndroid Build Coastguard Worker if (a == b) return true;
39*4d7e907cSAndroid Build Coastguard Worker if (a == ProgramType::AM && b == ProgramType::AM_HD) return true;
40*4d7e907cSAndroid Build Coastguard Worker if (a == ProgramType::AM_HD && b == ProgramType::AM) return true;
41*4d7e907cSAndroid Build Coastguard Worker if (a == ProgramType::FM && b == ProgramType::FM_HD) return true;
42*4d7e907cSAndroid Build Coastguard Worker if (a == ProgramType::FM_HD && b == ProgramType::FM) return true;
43*4d7e907cSAndroid Build Coastguard Worker return false;
44*4d7e907cSAndroid Build Coastguard Worker }
45*4d7e907cSAndroid Build Coastguard Worker
bothHaveId(const ProgramSelector & a,const ProgramSelector & b,const IdentifierType type)46*4d7e907cSAndroid Build Coastguard Worker static bool bothHaveId(const ProgramSelector& a, const ProgramSelector& b,
47*4d7e907cSAndroid Build Coastguard Worker const IdentifierType type) {
48*4d7e907cSAndroid Build Coastguard Worker return hasId(a, type) && hasId(b, type);
49*4d7e907cSAndroid Build Coastguard Worker }
50*4d7e907cSAndroid Build Coastguard Worker
anyHaveId(const ProgramSelector & a,const ProgramSelector & b,const IdentifierType type)51*4d7e907cSAndroid Build Coastguard Worker static bool anyHaveId(const ProgramSelector& a, const ProgramSelector& b,
52*4d7e907cSAndroid Build Coastguard Worker const IdentifierType type) {
53*4d7e907cSAndroid Build Coastguard Worker return hasId(a, type) || hasId(b, type);
54*4d7e907cSAndroid Build Coastguard Worker }
55*4d7e907cSAndroid Build Coastguard Worker
haveEqualIds(const ProgramSelector & a,const ProgramSelector & b,const IdentifierType type)56*4d7e907cSAndroid Build Coastguard Worker static bool haveEqualIds(const ProgramSelector& a, const ProgramSelector& b,
57*4d7e907cSAndroid Build Coastguard Worker const IdentifierType type) {
58*4d7e907cSAndroid Build Coastguard Worker if (!bothHaveId(a, b, type)) return false;
59*4d7e907cSAndroid Build Coastguard Worker /* We should check all Ids of a given type (ie. other AF),
60*4d7e907cSAndroid Build Coastguard Worker * but it doesn't matter for default implementation.
61*4d7e907cSAndroid Build Coastguard Worker */
62*4d7e907cSAndroid Build Coastguard Worker return getId(a, type) == getId(b, type);
63*4d7e907cSAndroid Build Coastguard Worker }
64*4d7e907cSAndroid Build Coastguard Worker
tunesTo(const ProgramSelector & a,const ProgramSelector & b)65*4d7e907cSAndroid Build Coastguard Worker bool tunesTo(const ProgramSelector& a, const ProgramSelector& b) {
66*4d7e907cSAndroid Build Coastguard Worker if (!isCompatibleProgramType(a.programType, b.programType)) return false;
67*4d7e907cSAndroid Build Coastguard Worker
68*4d7e907cSAndroid Build Coastguard Worker auto type = getType(a);
69*4d7e907cSAndroid Build Coastguard Worker
70*4d7e907cSAndroid Build Coastguard Worker switch (type) {
71*4d7e907cSAndroid Build Coastguard Worker case ProgramType::AM:
72*4d7e907cSAndroid Build Coastguard Worker case ProgramType::AM_HD:
73*4d7e907cSAndroid Build Coastguard Worker case ProgramType::FM:
74*4d7e907cSAndroid Build Coastguard Worker case ProgramType::FM_HD:
75*4d7e907cSAndroid Build Coastguard Worker if (haveEqualIds(a, b, IdentifierType::HD_STATION_ID_EXT)) return true;
76*4d7e907cSAndroid Build Coastguard Worker
77*4d7e907cSAndroid Build Coastguard Worker // if HD Radio subchannel is specified, it must match
78*4d7e907cSAndroid Build Coastguard Worker if (anyHaveId(a, b, IdentifierType::HD_SUBCHANNEL)) {
79*4d7e907cSAndroid Build Coastguard Worker // missing subchannel (analog) is an equivalent of first subchannel (MPS)
80*4d7e907cSAndroid Build Coastguard Worker auto aCh = getId(a, IdentifierType::HD_SUBCHANNEL, 0);
81*4d7e907cSAndroid Build Coastguard Worker auto bCh = getId(b, IdentifierType::HD_SUBCHANNEL, 0);
82*4d7e907cSAndroid Build Coastguard Worker if (aCh != bCh) return false;
83*4d7e907cSAndroid Build Coastguard Worker }
84*4d7e907cSAndroid Build Coastguard Worker
85*4d7e907cSAndroid Build Coastguard Worker if (haveEqualIds(a, b, IdentifierType::RDS_PI)) return true;
86*4d7e907cSAndroid Build Coastguard Worker
87*4d7e907cSAndroid Build Coastguard Worker return haveEqualIds(a, b, IdentifierType::AMFM_FREQUENCY);
88*4d7e907cSAndroid Build Coastguard Worker case ProgramType::DAB:
89*4d7e907cSAndroid Build Coastguard Worker return haveEqualIds(a, b, IdentifierType::DAB_SIDECC);
90*4d7e907cSAndroid Build Coastguard Worker case ProgramType::DRMO:
91*4d7e907cSAndroid Build Coastguard Worker return haveEqualIds(a, b, IdentifierType::DRMO_SERVICE_ID);
92*4d7e907cSAndroid Build Coastguard Worker case ProgramType::SXM:
93*4d7e907cSAndroid Build Coastguard Worker if (anyHaveId(a, b, IdentifierType::SXM_SERVICE_ID)) {
94*4d7e907cSAndroid Build Coastguard Worker return haveEqualIds(a, b, IdentifierType::SXM_SERVICE_ID);
95*4d7e907cSAndroid Build Coastguard Worker }
96*4d7e907cSAndroid Build Coastguard Worker return haveEqualIds(a, b, IdentifierType::SXM_CHANNEL);
97*4d7e907cSAndroid Build Coastguard Worker default: // includes all vendor types
98*4d7e907cSAndroid Build Coastguard Worker ALOGW("Unsupported program type: %s", toString(type).c_str());
99*4d7e907cSAndroid Build Coastguard Worker return false;
100*4d7e907cSAndroid Build Coastguard Worker }
101*4d7e907cSAndroid Build Coastguard Worker }
102*4d7e907cSAndroid Build Coastguard Worker
getType(const ProgramSelector & sel)103*4d7e907cSAndroid Build Coastguard Worker ProgramType getType(const ProgramSelector& sel) {
104*4d7e907cSAndroid Build Coastguard Worker return static_cast<ProgramType>(sel.programType);
105*4d7e907cSAndroid Build Coastguard Worker }
106*4d7e907cSAndroid Build Coastguard Worker
isAmFm(const ProgramType type)107*4d7e907cSAndroid Build Coastguard Worker bool isAmFm(const ProgramType type) {
108*4d7e907cSAndroid Build Coastguard Worker switch (type) {
109*4d7e907cSAndroid Build Coastguard Worker case ProgramType::AM:
110*4d7e907cSAndroid Build Coastguard Worker case ProgramType::FM:
111*4d7e907cSAndroid Build Coastguard Worker case ProgramType::AM_HD:
112*4d7e907cSAndroid Build Coastguard Worker case ProgramType::FM_HD:
113*4d7e907cSAndroid Build Coastguard Worker return true;
114*4d7e907cSAndroid Build Coastguard Worker default:
115*4d7e907cSAndroid Build Coastguard Worker return false;
116*4d7e907cSAndroid Build Coastguard Worker }
117*4d7e907cSAndroid Build Coastguard Worker }
118*4d7e907cSAndroid Build Coastguard Worker
isAm(const Band band)119*4d7e907cSAndroid Build Coastguard Worker bool isAm(const Band band) {
120*4d7e907cSAndroid Build Coastguard Worker return band == Band::AM || band == Band::AM_HD;
121*4d7e907cSAndroid Build Coastguard Worker }
122*4d7e907cSAndroid Build Coastguard Worker
isFm(const Band band)123*4d7e907cSAndroid Build Coastguard Worker bool isFm(const Band band) {
124*4d7e907cSAndroid Build Coastguard Worker return band == Band::FM || band == Band::FM_HD;
125*4d7e907cSAndroid Build Coastguard Worker }
126*4d7e907cSAndroid Build Coastguard Worker
maybeGetId(const ProgramSelector & sel,const IdentifierType type,uint64_t * val)127*4d7e907cSAndroid Build Coastguard Worker static bool maybeGetId(const ProgramSelector& sel, const IdentifierType type, uint64_t* val) {
128*4d7e907cSAndroid Build Coastguard Worker auto itype = static_cast<uint32_t>(type);
129*4d7e907cSAndroid Build Coastguard Worker
130*4d7e907cSAndroid Build Coastguard Worker if (sel.primaryId.type == itype) {
131*4d7e907cSAndroid Build Coastguard Worker if (val) *val = sel.primaryId.value;
132*4d7e907cSAndroid Build Coastguard Worker return true;
133*4d7e907cSAndroid Build Coastguard Worker }
134*4d7e907cSAndroid Build Coastguard Worker
135*4d7e907cSAndroid Build Coastguard Worker // not optimal, but we don't care in default impl
136*4d7e907cSAndroid Build Coastguard Worker for (auto&& id : sel.secondaryIds) {
137*4d7e907cSAndroid Build Coastguard Worker if (id.type == itype) {
138*4d7e907cSAndroid Build Coastguard Worker if (val) *val = id.value;
139*4d7e907cSAndroid Build Coastguard Worker return true;
140*4d7e907cSAndroid Build Coastguard Worker }
141*4d7e907cSAndroid Build Coastguard Worker }
142*4d7e907cSAndroid Build Coastguard Worker
143*4d7e907cSAndroid Build Coastguard Worker return false;
144*4d7e907cSAndroid Build Coastguard Worker }
145*4d7e907cSAndroid Build Coastguard Worker
hasId(const ProgramSelector & sel,const IdentifierType type)146*4d7e907cSAndroid Build Coastguard Worker bool hasId(const ProgramSelector& sel, const IdentifierType type) {
147*4d7e907cSAndroid Build Coastguard Worker return maybeGetId(sel, type, nullptr);
148*4d7e907cSAndroid Build Coastguard Worker }
149*4d7e907cSAndroid Build Coastguard Worker
getId(const ProgramSelector & sel,const IdentifierType type)150*4d7e907cSAndroid Build Coastguard Worker uint64_t getId(const ProgramSelector& sel, const IdentifierType type) {
151*4d7e907cSAndroid Build Coastguard Worker uint64_t val;
152*4d7e907cSAndroid Build Coastguard Worker
153*4d7e907cSAndroid Build Coastguard Worker if (maybeGetId(sel, type, &val)) {
154*4d7e907cSAndroid Build Coastguard Worker return val;
155*4d7e907cSAndroid Build Coastguard Worker }
156*4d7e907cSAndroid Build Coastguard Worker
157*4d7e907cSAndroid Build Coastguard Worker ALOGW("Identifier %s not found", toString(type).c_str());
158*4d7e907cSAndroid Build Coastguard Worker return 0;
159*4d7e907cSAndroid Build Coastguard Worker }
160*4d7e907cSAndroid Build Coastguard Worker
getId(const ProgramSelector & sel,const IdentifierType type,uint64_t defval)161*4d7e907cSAndroid Build Coastguard Worker uint64_t getId(const ProgramSelector& sel, const IdentifierType type, uint64_t defval) {
162*4d7e907cSAndroid Build Coastguard Worker if (!hasId(sel, type)) return defval;
163*4d7e907cSAndroid Build Coastguard Worker return getId(sel, type);
164*4d7e907cSAndroid Build Coastguard Worker }
165*4d7e907cSAndroid Build Coastguard Worker
make_selector(Band band,uint32_t channel,uint32_t subChannel)166*4d7e907cSAndroid Build Coastguard Worker ProgramSelector make_selector(Band band, uint32_t channel, uint32_t subChannel) {
167*4d7e907cSAndroid Build Coastguard Worker ProgramSelector sel = {};
168*4d7e907cSAndroid Build Coastguard Worker
169*4d7e907cSAndroid Build Coastguard Worker ALOGW_IF((subChannel > 0) && (band == Band::AM || band == Band::FM),
170*4d7e907cSAndroid Build Coastguard Worker "got subChannel for non-HD AM/FM");
171*4d7e907cSAndroid Build Coastguard Worker
172*4d7e907cSAndroid Build Coastguard Worker // we can't use ProgramType::AM_HD or FM_HD, because we don't know HD station ID
173*4d7e907cSAndroid Build Coastguard Worker ProgramType type;
174*4d7e907cSAndroid Build Coastguard Worker if (isAm(band)) {
175*4d7e907cSAndroid Build Coastguard Worker type = ProgramType::AM;
176*4d7e907cSAndroid Build Coastguard Worker } else if (isFm(band)) {
177*4d7e907cSAndroid Build Coastguard Worker type = ProgramType::FM;
178*4d7e907cSAndroid Build Coastguard Worker } else {
179*4d7e907cSAndroid Build Coastguard Worker LOG_ALWAYS_FATAL("Unsupported band: %s", toString(band).c_str());
180*4d7e907cSAndroid Build Coastguard Worker }
181*4d7e907cSAndroid Build Coastguard Worker
182*4d7e907cSAndroid Build Coastguard Worker sel.programType = static_cast<uint32_t>(type);
183*4d7e907cSAndroid Build Coastguard Worker sel.primaryId.type = static_cast<uint32_t>(IdentifierType::AMFM_FREQUENCY);
184*4d7e907cSAndroid Build Coastguard Worker sel.primaryId.value = channel;
185*4d7e907cSAndroid Build Coastguard Worker if (subChannel > 0) {
186*4d7e907cSAndroid Build Coastguard Worker /* stating sub channel for AM/FM channel does not give any guarantees,
187*4d7e907cSAndroid Build Coastguard Worker * but we can't do much more without HD station ID
188*4d7e907cSAndroid Build Coastguard Worker *
189*4d7e907cSAndroid Build Coastguard Worker * The legacy APIs had 1-based subChannels, while ProgramSelector is 0-based.
190*4d7e907cSAndroid Build Coastguard Worker */
191*4d7e907cSAndroid Build Coastguard Worker sel.secondaryIds = hidl_vec<ProgramIdentifier>{
192*4d7e907cSAndroid Build Coastguard Worker {static_cast<uint32_t>(IdentifierType::HD_SUBCHANNEL), subChannel - 1},
193*4d7e907cSAndroid Build Coastguard Worker };
194*4d7e907cSAndroid Build Coastguard Worker }
195*4d7e907cSAndroid Build Coastguard Worker
196*4d7e907cSAndroid Build Coastguard Worker return sel;
197*4d7e907cSAndroid Build Coastguard Worker }
198*4d7e907cSAndroid Build Coastguard Worker
getLegacyChannel(const ProgramSelector & sel,uint32_t * channelOut,uint32_t * subChannelOut)199*4d7e907cSAndroid Build Coastguard Worker bool getLegacyChannel(const ProgramSelector& sel, uint32_t* channelOut, uint32_t* subChannelOut) {
200*4d7e907cSAndroid Build Coastguard Worker if (channelOut) *channelOut = 0;
201*4d7e907cSAndroid Build Coastguard Worker if (subChannelOut) *subChannelOut = 0;
202*4d7e907cSAndroid Build Coastguard Worker if (isAmFm(getType(sel))) {
203*4d7e907cSAndroid Build Coastguard Worker if (channelOut) *channelOut = getId(sel, IdentifierType::AMFM_FREQUENCY);
204*4d7e907cSAndroid Build Coastguard Worker if (subChannelOut && hasId(sel, IdentifierType::HD_SUBCHANNEL)) {
205*4d7e907cSAndroid Build Coastguard Worker // The legacy APIs had 1-based subChannels, while ProgramSelector is 0-based.
206*4d7e907cSAndroid Build Coastguard Worker *subChannelOut = getId(sel, IdentifierType::HD_SUBCHANNEL) + 1;
207*4d7e907cSAndroid Build Coastguard Worker }
208*4d7e907cSAndroid Build Coastguard Worker return true;
209*4d7e907cSAndroid Build Coastguard Worker }
210*4d7e907cSAndroid Build Coastguard Worker return false;
211*4d7e907cSAndroid Build Coastguard Worker }
212*4d7e907cSAndroid Build Coastguard Worker
isDigital(const ProgramSelector & sel)213*4d7e907cSAndroid Build Coastguard Worker bool isDigital(const ProgramSelector& sel) {
214*4d7e907cSAndroid Build Coastguard Worker switch (getType(sel)) {
215*4d7e907cSAndroid Build Coastguard Worker case ProgramType::AM:
216*4d7e907cSAndroid Build Coastguard Worker case ProgramType::FM:
217*4d7e907cSAndroid Build Coastguard Worker return false;
218*4d7e907cSAndroid Build Coastguard Worker default:
219*4d7e907cSAndroid Build Coastguard Worker // VENDOR might not be digital, but it doesn't matter for default impl.
220*4d7e907cSAndroid Build Coastguard Worker return true;
221*4d7e907cSAndroid Build Coastguard Worker }
222*4d7e907cSAndroid Build Coastguard Worker }
223*4d7e907cSAndroid Build Coastguard Worker
224*4d7e907cSAndroid Build Coastguard Worker } // namespace utils
225*4d7e907cSAndroid Build Coastguard Worker
226*4d7e907cSAndroid Build Coastguard Worker namespace V1_0 {
227*4d7e907cSAndroid Build Coastguard Worker
operator ==(const BandConfig & l,const BandConfig & r)228*4d7e907cSAndroid Build Coastguard Worker bool operator==(const BandConfig& l, const BandConfig& r) {
229*4d7e907cSAndroid Build Coastguard Worker using namespace utils;
230*4d7e907cSAndroid Build Coastguard Worker
231*4d7e907cSAndroid Build Coastguard Worker if (l.type != r.type) return false;
232*4d7e907cSAndroid Build Coastguard Worker if (l.antennaConnected != r.antennaConnected) return false;
233*4d7e907cSAndroid Build Coastguard Worker if (l.lowerLimit != r.lowerLimit) return false;
234*4d7e907cSAndroid Build Coastguard Worker if (l.upperLimit != r.upperLimit) return false;
235*4d7e907cSAndroid Build Coastguard Worker if (l.spacings != r.spacings) return false;
236*4d7e907cSAndroid Build Coastguard Worker if (isAm(l.type)) {
237*4d7e907cSAndroid Build Coastguard Worker return l.ext.am == r.ext.am;
238*4d7e907cSAndroid Build Coastguard Worker } else if (isFm(l.type)) {
239*4d7e907cSAndroid Build Coastguard Worker return l.ext.fm == r.ext.fm;
240*4d7e907cSAndroid Build Coastguard Worker } else {
241*4d7e907cSAndroid Build Coastguard Worker ALOGW("Unsupported band config type: %s", toString(l.type).c_str());
242*4d7e907cSAndroid Build Coastguard Worker return false;
243*4d7e907cSAndroid Build Coastguard Worker }
244*4d7e907cSAndroid Build Coastguard Worker }
245*4d7e907cSAndroid Build Coastguard Worker
246*4d7e907cSAndroid Build Coastguard Worker } // namespace V1_0
247*4d7e907cSAndroid Build Coastguard Worker } // namespace broadcastradio
248*4d7e907cSAndroid Build Coastguard Worker } // namespace hardware
249*4d7e907cSAndroid Build Coastguard Worker } // namespace android
250