blob: 7a59d6a13a863c5d4062ce276d6f8a01664b55ea [file] [log] [blame]
Tomasz Wasilczyka02b6ef2017-07-05 11:23:30 -07001/*
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
Tomasz Wasilczyk06100b32017-12-04 09:53:32 -080019#include <broadcastradio-utils-1x/Utils.h>
Tomasz Wasilczyka02b6ef2017-07-05 11:23:30 -070020
21#include <log/log.h>
22
23namespace android {
24namespace hardware {
25namespace broadcastradio {
Tomasz Wasilczyka02b6ef2017-07-05 11:23:30 -070026namespace utils {
27
28using V1_0::Band;
Tomasz Wasilczykf679e8b2017-09-14 09:43:35 -070029using V1_1::ProgramIdentifier;
30using V1_1::ProgramSelector;
31using V1_1::ProgramType;
Tomasz Wasilczyk002151c2017-11-22 09:43:06 -080032using V1_2::IdentifierType;
Tomasz Wasilczyka02b6ef2017-07-05 11:23:30 -070033
34static 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
46static bool bothHaveId(const ProgramSelector& a, const ProgramSelector& b,
47 const IdentifierType type) {
48 return hasId(a, type) && hasId(b, type);
49}
50
51static bool anyHaveId(const ProgramSelector& a, const ProgramSelector& b,
52 const IdentifierType type) {
53 return hasId(a, type) || hasId(b, type);
54}
55
56static bool haveEqualIds(const ProgramSelector& a, const ProgramSelector& b,
57 const IdentifierType type) {
58 if (!bothHaveId(a, b, type)) return false;
Tomasz Wasilczyk753c1d12017-07-25 14:55:49 -070059 /* We should check all Ids of a given type (ie. other AF),
60 * but it doesn't matter for default implementation.
61 */
Tomasz Wasilczyk06100b32017-12-04 09:53:32 -080062 return getId(a, type) == getId(b, type);
Tomasz Wasilczyka02b6ef2017-07-05 11:23:30 -070063}
64
65bool 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:
Tomasz Wasilczyk002151c2017-11-22 09:43:06 -080089 return haveEqualIds(a, b, IdentifierType::DAB_SID_EXT);
Tomasz Wasilczyka02b6ef2017-07-05 11:23:30 -070090 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);
Tomasz Wasilczykda97a6d2017-08-04 12:57:29 -070097 default: // includes all vendor types
Tomasz Wasilczyka02b6ef2017-07-05 11:23:30 -070098 ALOGW("Unsupported program type: %s", toString(type).c_str());
99 return false;
100 }
101}
102
103ProgramType getType(const ProgramSelector& sel) {
104 return static_cast<ProgramType>(sel.programType);
105}
106
107bool 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
Tomasz Wasilczyk701a5bd2017-08-10 12:32:45 -0700119bool isAm(const Band band) {
120 return band == Band::AM || band == Band::AM_HD;
121}
122
123bool isFm(const Band band) {
124 return band == Band::FM || band == Band::FM_HD;
125}
126
Tomasz Wasilczyk002151c2017-11-22 09:43:06 -0800127static bool maybeGetId(const ProgramSelector& sel, const IdentifierType type, uint64_t* val) {
Tomasz Wasilczyka02b6ef2017-07-05 11:23:30 -0700128 auto itype = static_cast<uint32_t>(type);
Tomasz Wasilczyk002151c2017-11-22 09:43:06 -0800129 auto itypeAlt = itype;
130 if (type == IdentifierType::DAB_SIDECC) {
131 itypeAlt = static_cast<uint32_t>(IdentifierType::DAB_SID_EXT);
Tomasz Wasilczyka02b6ef2017-07-05 11:23:30 -0700132 }
Tomasz Wasilczyk002151c2017-11-22 09:43:06 -0800133 if (type == IdentifierType::DAB_SID_EXT) {
134 itypeAlt = static_cast<uint32_t>(IdentifierType::DAB_SIDECC);
135 }
136
137 if (sel.primaryId.type == itype || sel.primaryId.type == itypeAlt) {
138 if (val) *val = sel.primaryId.value;
139 return true;
140 }
141
142 // not optimal, but we don't care in default impl
143 bool gotAlt = false;
144 for (auto&& id : sel.secondaryIds) {
145 if (id.type == itype) {
146 if (val) *val = id.value;
147 return true;
148 }
149 // alternative identifier is a backup, we prefer original value
150 if (id.type == itypeAlt) {
151 if (val) *val = id.value;
152 gotAlt = true;
153 continue;
154 }
155 }
156
157 return gotAlt;
158}
159
160bool hasId(const ProgramSelector& sel, const IdentifierType type) {
161 return maybeGetId(sel, type, nullptr);
Tomasz Wasilczyka02b6ef2017-07-05 11:23:30 -0700162}
163
164uint64_t getId(const ProgramSelector& sel, const IdentifierType type) {
Tomasz Wasilczyk002151c2017-11-22 09:43:06 -0800165 uint64_t val;
166
167 if (maybeGetId(sel, type, &val)) {
168 return val;
Tomasz Wasilczyka02b6ef2017-07-05 11:23:30 -0700169 }
Tomasz Wasilczyk002151c2017-11-22 09:43:06 -0800170
Tomasz Wasilczyka02b6ef2017-07-05 11:23:30 -0700171 ALOGW("Identifier %s not found", toString(type).c_str());
172 return 0;
173}
174
175uint64_t getId(const ProgramSelector& sel, const IdentifierType type, uint64_t defval) {
176 if (!hasId(sel, type)) return defval;
177 return getId(sel, type);
178}
179
180ProgramSelector make_selector(Band band, uint32_t channel, uint32_t subChannel) {
181 ProgramSelector sel = {};
182
183 ALOGW_IF((subChannel > 0) && (band == Band::AM || band == Band::FM),
184 "got subChannel for non-HD AM/FM");
185
186 // we can't use ProgramType::AM_HD or FM_HD, because we don't know HD station ID
187 ProgramType type;
Tomasz Wasilczyk701a5bd2017-08-10 12:32:45 -0700188 if (isAm(band)) {
189 type = ProgramType::AM;
190 } else if (isFm(band)) {
191 type = ProgramType::FM;
192 } else {
193 LOG_ALWAYS_FATAL("Unsupported band: %s", toString(band).c_str());
Tomasz Wasilczyka02b6ef2017-07-05 11:23:30 -0700194 }
195
196 sel.programType = static_cast<uint32_t>(type);
197 sel.primaryId.type = static_cast<uint32_t>(IdentifierType::AMFM_FREQUENCY);
198 sel.primaryId.value = channel;
199 if (subChannel > 0) {
Tomasz Wasilczyk2834b952017-07-12 14:17:09 -0700200 /* stating sub channel for AM/FM channel does not give any guarantees,
201 * but we can't do much more without HD station ID
202 *
203 * The legacy APIs had 1-based subChannels, while ProgramSelector is 0-based.
204 */
Tomasz Wasilczyka02b6ef2017-07-05 11:23:30 -0700205 sel.secondaryIds = hidl_vec<ProgramIdentifier>{
Tomasz Wasilczyk2834b952017-07-12 14:17:09 -0700206 {static_cast<uint32_t>(IdentifierType::HD_SUBCHANNEL), subChannel - 1},
Tomasz Wasilczyka02b6ef2017-07-05 11:23:30 -0700207 };
208 }
209
210 return sel;
211}
212
Tomasz Wasilczyk2834b952017-07-12 14:17:09 -0700213bool getLegacyChannel(const ProgramSelector& sel, uint32_t* channelOut, uint32_t* subChannelOut) {
214 if (channelOut) *channelOut = 0;
215 if (subChannelOut) *subChannelOut = 0;
Tomasz Wasilczyka02b6ef2017-07-05 11:23:30 -0700216 if (isAmFm(getType(sel))) {
Tomasz Wasilczyk2834b952017-07-12 14:17:09 -0700217 if (channelOut) *channelOut = getId(sel, IdentifierType::AMFM_FREQUENCY);
218 if (subChannelOut && hasId(sel, IdentifierType::HD_SUBCHANNEL)) {
219 // The legacy APIs had 1-based subChannels, while ProgramSelector is 0-based.
220 *subChannelOut = getId(sel, IdentifierType::HD_SUBCHANNEL) + 1;
221 }
Tomasz Wasilczyka02b6ef2017-07-05 11:23:30 -0700222 return true;
Tomasz Wasilczyka02b6ef2017-07-05 11:23:30 -0700223 }
Tomasz Wasilczyk2834b952017-07-12 14:17:09 -0700224 return false;
Tomasz Wasilczyka02b6ef2017-07-05 11:23:30 -0700225}
226
227bool isDigital(const ProgramSelector& sel) {
228 switch (getType(sel)) {
229 case ProgramType::AM:
230 case ProgramType::FM:
231 return false;
232 default:
233 // VENDOR might not be digital, but it doesn't matter for default impl.
234 return true;
235 }
236}
237
238} // namespace utils
Tomasz Wasilczykc1763a62017-07-25 10:01:17 -0700239
240namespace V1_0 {
241
242bool operator==(const BandConfig& l, const BandConfig& r) {
Tomasz Wasilczyk06100b32017-12-04 09:53:32 -0800243 using namespace utils;
244
Tomasz Wasilczykc1763a62017-07-25 10:01:17 -0700245 if (l.type != r.type) return false;
246 if (l.antennaConnected != r.antennaConnected) return false;
247 if (l.lowerLimit != r.lowerLimit) return false;
248 if (l.upperLimit != r.upperLimit) return false;
249 if (l.spacings != r.spacings) return false;
Tomasz Wasilczyk06100b32017-12-04 09:53:32 -0800250 if (isAm(l.type)) {
Tomasz Wasilczykc1763a62017-07-25 10:01:17 -0700251 return l.ext.am == r.ext.am;
Tomasz Wasilczyk06100b32017-12-04 09:53:32 -0800252 } else if (isFm(l.type)) {
Tomasz Wasilczykc1763a62017-07-25 10:01:17 -0700253 return l.ext.fm == r.ext.fm;
254 } else {
255 ALOGW("Unsupported band config type: %s", toString(l.type).c_str());
256 return false;
257 }
258}
259
260} // namespace V1_0
Tomasz Wasilczyka02b6ef2017-07-05 11:23:30 -0700261} // namespace broadcastradio
262} // namespace hardware
263} // namespace android