blob: e880ca05c9625fdadd19e40cdb0c6dcce760a9f9 [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 Wasilczykc1763a62017-07-25 10:01:17 -070019#include <broadcastradio-utils/Utils.h>
Tomasz Wasilczyka02b6ef2017-07-05 11:23:30 -070020
21#include <log/log.h>
22
23namespace android {
24namespace hardware {
25namespace broadcastradio {
26namespace V1_1 {
Tomasz Wasilczyka02b6ef2017-07-05 11:23:30 -070027namespace utils {
28
29using V1_0::Band;
30
31static bool isCompatibleProgramType(const uint32_t ia, const uint32_t ib) {
32 auto a = static_cast<ProgramType>(ia);
33 auto b = static_cast<ProgramType>(ib);
34
35 if (a == b) return true;
36 if (a == ProgramType::AM && b == ProgramType::AM_HD) return true;
37 if (a == ProgramType::AM_HD && b == ProgramType::AM) return true;
38 if (a == ProgramType::FM && b == ProgramType::FM_HD) return true;
39 if (a == ProgramType::FM_HD && b == ProgramType::FM) return true;
40 return false;
41}
42
43static bool bothHaveId(const ProgramSelector& a, const ProgramSelector& b,
44 const IdentifierType type) {
45 return hasId(a, type) && hasId(b, type);
46}
47
48static bool anyHaveId(const ProgramSelector& a, const ProgramSelector& b,
49 const IdentifierType type) {
50 return hasId(a, type) || hasId(b, type);
51}
52
53static bool haveEqualIds(const ProgramSelector& a, const ProgramSelector& b,
54 const IdentifierType type) {
55 if (!bothHaveId(a, b, type)) return false;
Tomasz Wasilczyk753c1d12017-07-25 14:55:49 -070056 /* We should check all Ids of a given type (ie. other AF),
57 * but it doesn't matter for default implementation.
58 */
Tomasz Wasilczyka02b6ef2017-07-05 11:23:30 -070059 auto aId = getId(a, type);
60 auto bId = getId(b, type);
61 return aId == bId;
62}
63
64bool tunesTo(const ProgramSelector& a, const ProgramSelector& b) {
65 if (!isCompatibleProgramType(a.programType, b.programType)) return false;
66
67 auto type = getType(a);
68
69 switch (type) {
70 case ProgramType::AM:
71 case ProgramType::AM_HD:
72 case ProgramType::FM:
73 case ProgramType::FM_HD:
74 if (haveEqualIds(a, b, IdentifierType::HD_STATION_ID_EXT)) return true;
75
76 // if HD Radio subchannel is specified, it must match
77 if (anyHaveId(a, b, IdentifierType::HD_SUBCHANNEL)) {
78 // missing subchannel (analog) is an equivalent of first subchannel (MPS)
79 auto aCh = getId(a, IdentifierType::HD_SUBCHANNEL, 0);
80 auto bCh = getId(b, IdentifierType::HD_SUBCHANNEL, 0);
81 if (aCh != bCh) return false;
82 }
83
84 if (haveEqualIds(a, b, IdentifierType::RDS_PI)) return true;
85
86 return haveEqualIds(a, b, IdentifierType::AMFM_FREQUENCY);
87 case ProgramType::DAB:
88 return haveEqualIds(a, b, IdentifierType::DAB_SIDECC);
89 case ProgramType::DRMO:
90 return haveEqualIds(a, b, IdentifierType::DRMO_SERVICE_ID);
91 case ProgramType::SXM:
92 if (anyHaveId(a, b, IdentifierType::SXM_SERVICE_ID)) {
93 return haveEqualIds(a, b, IdentifierType::SXM_SERVICE_ID);
94 }
95 return haveEqualIds(a, b, IdentifierType::SXM_CHANNEL);
Tomasz Wasilczykd167caf2017-07-17 16:15:25 -070096 case ProgramType::VENDOR1:
97 case ProgramType::VENDOR2:
98 case ProgramType::VENDOR3:
99 case ProgramType::VENDOR4:
Tomasz Wasilczyka02b6ef2017-07-05 11:23:30 -0700100 default:
101 ALOGW("Unsupported program type: %s", toString(type).c_str());
102 return false;
103 }
104}
105
106ProgramType getType(const ProgramSelector& sel) {
107 return static_cast<ProgramType>(sel.programType);
108}
109
110bool isAmFm(const ProgramType type) {
111 switch (type) {
112 case ProgramType::AM:
113 case ProgramType::FM:
114 case ProgramType::AM_HD:
115 case ProgramType::FM_HD:
116 return true;
117 default:
118 return false;
119 }
120}
121
Tomasz Wasilczyk701a5bd2017-08-10 12:32:45 -0700122bool isAm(const Band band) {
123 return band == Band::AM || band == Band::AM_HD;
124}
125
126bool isFm(const Band band) {
127 return band == Band::FM || band == Band::FM_HD;
128}
129
Tomasz Wasilczyka02b6ef2017-07-05 11:23:30 -0700130bool hasId(const ProgramSelector& sel, const IdentifierType type) {
131 auto itype = static_cast<uint32_t>(type);
132 if (sel.primaryId.type == itype) return true;
133 // not optimal, but we don't care in default impl
134 for (auto&& id : sel.secondaryIds) {
135 if (id.type == itype) return true;
136 }
137 return false;
138}
139
140uint64_t getId(const ProgramSelector& sel, const IdentifierType type) {
141 auto itype = static_cast<uint32_t>(type);
142 if (sel.primaryId.type == itype) return sel.primaryId.value;
143 // not optimal, but we don't care in default impl
144 for (auto&& id : sel.secondaryIds) {
145 if (id.type == itype) return id.value;
146 }
147 ALOGW("Identifier %s not found", toString(type).c_str());
148 return 0;
149}
150
151uint64_t getId(const ProgramSelector& sel, const IdentifierType type, uint64_t defval) {
152 if (!hasId(sel, type)) return defval;
153 return getId(sel, type);
154}
155
156ProgramSelector make_selector(Band band, uint32_t channel, uint32_t subChannel) {
157 ProgramSelector sel = {};
158
159 ALOGW_IF((subChannel > 0) && (band == Band::AM || band == Band::FM),
160 "got subChannel for non-HD AM/FM");
161
162 // we can't use ProgramType::AM_HD or FM_HD, because we don't know HD station ID
163 ProgramType type;
Tomasz Wasilczyk701a5bd2017-08-10 12:32:45 -0700164 if (isAm(band)) {
165 type = ProgramType::AM;
166 } else if (isFm(band)) {
167 type = ProgramType::FM;
168 } else {
169 LOG_ALWAYS_FATAL("Unsupported band: %s", toString(band).c_str());
Tomasz Wasilczyka02b6ef2017-07-05 11:23:30 -0700170 }
171
172 sel.programType = static_cast<uint32_t>(type);
173 sel.primaryId.type = static_cast<uint32_t>(IdentifierType::AMFM_FREQUENCY);
174 sel.primaryId.value = channel;
175 if (subChannel > 0) {
Tomasz Wasilczyk2834b952017-07-12 14:17:09 -0700176 /* stating sub channel for AM/FM channel does not give any guarantees,
177 * but we can't do much more without HD station ID
178 *
179 * The legacy APIs had 1-based subChannels, while ProgramSelector is 0-based.
180 */
Tomasz Wasilczyka02b6ef2017-07-05 11:23:30 -0700181 sel.secondaryIds = hidl_vec<ProgramIdentifier>{
Tomasz Wasilczyk2834b952017-07-12 14:17:09 -0700182 {static_cast<uint32_t>(IdentifierType::HD_SUBCHANNEL), subChannel - 1},
Tomasz Wasilczyka02b6ef2017-07-05 11:23:30 -0700183 };
184 }
185
186 return sel;
187}
188
Tomasz Wasilczyk2834b952017-07-12 14:17:09 -0700189bool getLegacyChannel(const ProgramSelector& sel, uint32_t* channelOut, uint32_t* subChannelOut) {
190 if (channelOut) *channelOut = 0;
191 if (subChannelOut) *subChannelOut = 0;
Tomasz Wasilczyka02b6ef2017-07-05 11:23:30 -0700192 if (isAmFm(getType(sel))) {
Tomasz Wasilczyk2834b952017-07-12 14:17:09 -0700193 if (channelOut) *channelOut = getId(sel, IdentifierType::AMFM_FREQUENCY);
194 if (subChannelOut && hasId(sel, IdentifierType::HD_SUBCHANNEL)) {
195 // The legacy APIs had 1-based subChannels, while ProgramSelector is 0-based.
196 *subChannelOut = getId(sel, IdentifierType::HD_SUBCHANNEL) + 1;
197 }
Tomasz Wasilczyka02b6ef2017-07-05 11:23:30 -0700198 return true;
Tomasz Wasilczyka02b6ef2017-07-05 11:23:30 -0700199 }
Tomasz Wasilczyk2834b952017-07-12 14:17:09 -0700200 return false;
Tomasz Wasilczyka02b6ef2017-07-05 11:23:30 -0700201}
202
203bool isDigital(const ProgramSelector& sel) {
204 switch (getType(sel)) {
205 case ProgramType::AM:
206 case ProgramType::FM:
207 return false;
208 default:
209 // VENDOR might not be digital, but it doesn't matter for default impl.
210 return true;
211 }
212}
213
214} // namespace utils
Tomasz Wasilczyka02b6ef2017-07-05 11:23:30 -0700215} // namespace V1_1
Tomasz Wasilczykc1763a62017-07-25 10:01:17 -0700216
217namespace V1_0 {
218
219bool operator==(const BandConfig& l, const BandConfig& r) {
220 if (l.type != r.type) return false;
221 if (l.antennaConnected != r.antennaConnected) return false;
222 if (l.lowerLimit != r.lowerLimit) return false;
223 if (l.upperLimit != r.upperLimit) return false;
224 if (l.spacings != r.spacings) return false;
Tomasz Wasilczyk701a5bd2017-08-10 12:32:45 -0700225 if (V1_1::utils::isAm(l.type)) {
Tomasz Wasilczykc1763a62017-07-25 10:01:17 -0700226 return l.ext.am == r.ext.am;
Tomasz Wasilczyk701a5bd2017-08-10 12:32:45 -0700227 } else if (V1_1::utils::isFm(l.type)) {
Tomasz Wasilczykc1763a62017-07-25 10:01:17 -0700228 return l.ext.fm == r.ext.fm;
229 } else {
230 ALOGW("Unsupported band config type: %s", toString(l.type).c_str());
231 return false;
232 }
233}
234
235} // namespace V1_0
Tomasz Wasilczyka02b6ef2017-07-05 11:23:30 -0700236} // namespace broadcastradio
237} // namespace hardware
238} // namespace android