diff options
Diffstat (limited to 'broadcastradio/common/utils/Utils.cpp')
-rw-r--r-- | broadcastradio/common/utils/Utils.cpp | 236 |
1 files changed, 236 insertions, 0 deletions
diff --git a/broadcastradio/common/utils/Utils.cpp b/broadcastradio/common/utils/Utils.cpp new file mode 100644 index 00000000..bdaf8e8c --- /dev/null +++ b/broadcastradio/common/utils/Utils.cpp | |||
@@ -0,0 +1,236 @@ | |||
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/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 | |||
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 | |||
46 | static bool bothHaveId(const ProgramSelector& a, const ProgramSelector& b, | ||
47 | const IdentifierType type) { | ||
48 | return hasId(a, type) && hasId(b, type); | ||
49 | } | ||
50 | |||
51 | static bool anyHaveId(const ProgramSelector& a, const ProgramSelector& b, | ||
52 | const IdentifierType type) { | ||
53 | return hasId(a, type) || hasId(b, type); | ||
54 | } | ||
55 | |||
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 | auto aId = getId(a, type); | ||
63 | auto bId = getId(b, type); | ||
64 | return aId == bId; | ||
65 | } | ||
66 | |||
67 | bool tunesTo(const ProgramSelector& a, const ProgramSelector& b) { | ||
68 | if (!isCompatibleProgramType(a.programType, b.programType)) return false; | ||
69 | |||
70 | auto type = getType(a); | ||
71 | |||
72 | switch (type) { | ||
73 | case ProgramType::AM: | ||
74 | case ProgramType::AM_HD: | ||
75 | case ProgramType::FM: | ||
76 | case ProgramType::FM_HD: | ||
77 | if (haveEqualIds(a, b, IdentifierType::HD_STATION_ID_EXT)) return true; | ||
78 | |||
79 | // if HD Radio subchannel is specified, it must match | ||
80 | if (anyHaveId(a, b, IdentifierType::HD_SUBCHANNEL)) { | ||
81 | // missing subchannel (analog) is an equivalent of first subchannel (MPS) | ||
82 | auto aCh = getId(a, IdentifierType::HD_SUBCHANNEL, 0); | ||
83 | auto bCh = getId(b, IdentifierType::HD_SUBCHANNEL, 0); | ||
84 | if (aCh != bCh) return false; | ||
85 | } | ||
86 | |||
87 | if (haveEqualIds(a, b, IdentifierType::RDS_PI)) return true; | ||
88 | |||
89 | return haveEqualIds(a, b, IdentifierType::AMFM_FREQUENCY); | ||
90 | case ProgramType::DAB: | ||
91 | return haveEqualIds(a, b, IdentifierType::DAB_SIDECC); | ||
92 | case ProgramType::DRMO: | ||
93 | return haveEqualIds(a, b, IdentifierType::DRMO_SERVICE_ID); | ||
94 | case ProgramType::SXM: | ||
95 | if (anyHaveId(a, b, IdentifierType::SXM_SERVICE_ID)) { | ||
96 | return haveEqualIds(a, b, IdentifierType::SXM_SERVICE_ID); | ||
97 | } | ||
98 | return haveEqualIds(a, b, IdentifierType::SXM_CHANNEL); | ||
99 | default: // includes all vendor types | ||
100 | ALOGW("Unsupported program type: %s", toString(type).c_str()); | ||
101 | return false; | ||
102 | } | ||
103 | } | ||
104 | |||
105 | ProgramType getType(const ProgramSelector& sel) { | ||
106 | return static_cast<ProgramType>(sel.programType); | ||
107 | } | ||
108 | |||
109 | bool isAmFm(const ProgramType type) { | ||
110 | switch (type) { | ||
111 | case ProgramType::AM: | ||
112 | case ProgramType::FM: | ||
113 | case ProgramType::AM_HD: | ||
114 | case ProgramType::FM_HD: | ||
115 | return true; | ||
116 | default: | ||
117 | return false; | ||
118 | } | ||
119 | } | ||
120 | |||
121 | bool isAm(const Band band) { | ||
122 | return band == Band::AM || band == Band::AM_HD; | ||
123 | } | ||
124 | |||
125 | bool isFm(const Band band) { | ||
126 | return band == Band::FM || band == Band::FM_HD; | ||
127 | } | ||
128 | |||
129 | bool hasId(const ProgramSelector& sel, const IdentifierType type) { | ||
130 | auto itype = static_cast<uint32_t>(type); | ||
131 | if (sel.primaryId.type == itype) return true; | ||
132 | // not optimal, but we don't care in default impl | ||
133 | for (auto&& id : sel.secondaryIds) { | ||
134 | if (id.type == itype) return true; | ||
135 | } | ||
136 | return false; | ||
137 | } | ||
138 | |||
139 | uint64_t getId(const ProgramSelector& sel, const IdentifierType type) { | ||
140 | auto itype = static_cast<uint32_t>(type); | ||
141 | if (sel.primaryId.type == itype) return sel.primaryId.value; | ||
142 | // not optimal, but we don't care in default impl | ||
143 | for (auto&& id : sel.secondaryIds) { | ||
144 | if (id.type == itype) return id.value; | ||
145 | } | ||
146 | ALOGW("Identifier %s not found", toString(type).c_str()); | ||
147 | return 0; | ||
148 | } | ||
149 | |||
150 | uint64_t getId(const ProgramSelector& sel, const IdentifierType type, uint64_t defval) { | ||
151 | if (!hasId(sel, type)) return defval; | ||
152 | return getId(sel, type); | ||
153 | } | ||
154 | |||
155 | ProgramSelector make_selector(Band band, uint32_t channel, uint32_t subChannel) { | ||
156 | ProgramSelector sel = {}; | ||
157 | |||
158 | ALOGW_IF((subChannel > 0) && (band == Band::AM || band == Band::FM), | ||
159 | "got subChannel for non-HD AM/FM"); | ||
160 | |||
161 | // we can't use ProgramType::AM_HD or FM_HD, because we don't know HD station ID | ||
162 | ProgramType type; | ||
163 | if (isAm(band)) { | ||
164 | type = ProgramType::AM; | ||
165 | } else if (isFm(band)) { | ||
166 | type = ProgramType::FM; | ||
167 | } else { | ||
168 | LOG_ALWAYS_FATAL("Unsupported band: %s", toString(band).c_str()); | ||
169 | } | ||
170 | |||
171 | sel.programType = static_cast<uint32_t>(type); | ||
172 | sel.primaryId.type = static_cast<uint32_t>(IdentifierType::AMFM_FREQUENCY); | ||
173 | sel.primaryId.value = channel; | ||
174 | if (subChannel > 0) { | ||
175 | /* stating sub channel for AM/FM channel does not give any guarantees, | ||
176 | * but we can't do much more without HD station ID | ||
177 | * | ||
178 | * The legacy APIs had 1-based subChannels, while ProgramSelector is 0-based. | ||
179 | */ | ||
180 | sel.secondaryIds = hidl_vec<ProgramIdentifier>{ | ||
181 | {static_cast<uint32_t>(IdentifierType::HD_SUBCHANNEL), subChannel - 1}, | ||
182 | }; | ||
183 | } | ||
184 | |||
185 | return sel; | ||
186 | } | ||
187 | |||
188 | bool getLegacyChannel(const ProgramSelector& sel, uint32_t* channelOut, uint32_t* subChannelOut) { | ||
189 | if (channelOut) *channelOut = 0; | ||
190 | if (subChannelOut) *subChannelOut = 0; | ||
191 | if (isAmFm(getType(sel))) { | ||
192 | if (channelOut) *channelOut = getId(sel, IdentifierType::AMFM_FREQUENCY); | ||
193 | if (subChannelOut && hasId(sel, IdentifierType::HD_SUBCHANNEL)) { | ||
194 | // The legacy APIs had 1-based subChannels, while ProgramSelector is 0-based. | ||
195 | *subChannelOut = getId(sel, IdentifierType::HD_SUBCHANNEL) + 1; | ||
196 | } | ||
197 | return true; | ||
198 | } | ||
199 | return false; | ||
200 | } | ||
201 | |||
202 | bool isDigital(const ProgramSelector& sel) { | ||
203 | switch (getType(sel)) { | ||
204 | case ProgramType::AM: | ||
205 | case ProgramType::FM: | ||
206 | return false; | ||
207 | default: | ||
208 | // VENDOR might not be digital, but it doesn't matter for default impl. | ||
209 | return true; | ||
210 | } | ||
211 | } | ||
212 | |||
213 | } // namespace utils | ||
214 | |||
215 | namespace V1_0 { | ||
216 | |||
217 | bool operator==(const BandConfig& l, const BandConfig& r) { | ||
218 | if (l.type != r.type) return false; | ||
219 | if (l.antennaConnected != r.antennaConnected) return false; | ||
220 | if (l.lowerLimit != r.lowerLimit) return false; | ||
221 | if (l.upperLimit != r.upperLimit) return false; | ||
222 | if (l.spacings != r.spacings) return false; | ||
223 | if (utils::isAm(l.type)) { | ||
224 | return l.ext.am == r.ext.am; | ||
225 | } else if (utils::isFm(l.type)) { | ||
226 | return l.ext.fm == r.ext.fm; | ||
227 | } else { | ||
228 | ALOGW("Unsupported band config type: %s", toString(l.type).c_str()); | ||
229 | return false; | ||
230 | } | ||
231 | } | ||
232 | |||
233 | } // namespace V1_0 | ||
234 | } // namespace broadcastradio | ||
235 | } // namespace hardware | ||
236 | } // namespace android | ||