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