blob: 9dc0a53cd32daa11ecb414d12b4e1bf5f732fc0a [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
19#include "Utils.h"
20
21#include <log/log.h>
22
23namespace android {
24namespace hardware {
25namespace broadcastradio {
26namespace V1_1 {
27namespace implementation {
28namespace utils {
29
30using V1_0::Band;
31
32static bool isCompatibleProgramType(const uint32_t ia, const uint32_t ib) {
33 auto a = static_cast<ProgramType>(ia);
34 auto b = static_cast<ProgramType>(ib);
35
36 if (a == b) return true;
37 if (a == ProgramType::AM && b == ProgramType::AM_HD) return true;
38 if (a == ProgramType::AM_HD && b == ProgramType::AM) return true;
39 if (a == ProgramType::FM && b == ProgramType::FM_HD) return true;
40 if (a == ProgramType::FM_HD && b == ProgramType::FM) return true;
41 return false;
42}
43
44static bool bothHaveId(const ProgramSelector& a, const ProgramSelector& b,
45 const IdentifierType type) {
46 return hasId(a, type) && hasId(b, type);
47}
48
49static bool anyHaveId(const ProgramSelector& a, const ProgramSelector& b,
50 const IdentifierType type) {
51 return hasId(a, type) || hasId(b, type);
52}
53
54static bool haveEqualIds(const ProgramSelector& a, const ProgramSelector& b,
55 const IdentifierType type) {
56 if (!bothHaveId(a, b, type)) return false;
57 // TODO(b/36864090): we should check all Ids of a given type (ie. other AF), not just one
58 auto aId = getId(a, type);
59 auto bId = getId(b, type);
60 return aId == bId;
61}
62
63bool tunesTo(const ProgramSelector& a, const ProgramSelector& b) {
64 if (!isCompatibleProgramType(a.programType, b.programType)) return false;
65
66 auto type = getType(a);
67
68 switch (type) {
69 case ProgramType::AM:
70 case ProgramType::AM_HD:
71 case ProgramType::FM:
72 case ProgramType::FM_HD:
73 if (haveEqualIds(a, b, IdentifierType::HD_STATION_ID_EXT)) return true;
74
75 // if HD Radio subchannel is specified, it must match
76 if (anyHaveId(a, b, IdentifierType::HD_SUBCHANNEL)) {
77 // missing subchannel (analog) is an equivalent of first subchannel (MPS)
78 auto aCh = getId(a, IdentifierType::HD_SUBCHANNEL, 0);
79 auto bCh = getId(b, IdentifierType::HD_SUBCHANNEL, 0);
80 if (aCh != bCh) return false;
81 }
82
83 if (haveEqualIds(a, b, IdentifierType::RDS_PI)) return true;
84
85 return haveEqualIds(a, b, IdentifierType::AMFM_FREQUENCY);
86 case ProgramType::DAB:
87 return haveEqualIds(a, b, IdentifierType::DAB_SIDECC);
88 case ProgramType::DRMO:
89 return haveEqualIds(a, b, IdentifierType::DRMO_SERVICE_ID);
90 case ProgramType::SXM:
91 if (anyHaveId(a, b, IdentifierType::SXM_SERVICE_ID)) {
92 return haveEqualIds(a, b, IdentifierType::SXM_SERVICE_ID);
93 }
94 return haveEqualIds(a, b, IdentifierType::SXM_CHANNEL);
95 case ProgramType::VENDOR:
96 default:
97 ALOGW("Unsupported program type: %s", toString(type).c_str());
98 return false;
99 }
100}
101
102ProgramType getType(const ProgramSelector& sel) {
103 return static_cast<ProgramType>(sel.programType);
104}
105
106bool isAmFm(const ProgramType type) {
107 switch (type) {
108 case ProgramType::AM:
109 case ProgramType::FM:
110 case ProgramType::AM_HD:
111 case ProgramType::FM_HD:
112 return true;
113 default:
114 return false;
115 }
116}
117
118bool hasId(const ProgramSelector& sel, const IdentifierType type) {
119 auto itype = static_cast<uint32_t>(type);
120 if (sel.primaryId.type == itype) return true;
121 // not optimal, but we don't care in default impl
122 for (auto&& id : sel.secondaryIds) {
123 if (id.type == itype) return true;
124 }
125 return false;
126}
127
128uint64_t getId(const ProgramSelector& sel, const IdentifierType type) {
129 auto itype = static_cast<uint32_t>(type);
130 if (sel.primaryId.type == itype) return sel.primaryId.value;
131 // not optimal, but we don't care in default impl
132 for (auto&& id : sel.secondaryIds) {
133 if (id.type == itype) return id.value;
134 }
135 ALOGW("Identifier %s not found", toString(type).c_str());
136 return 0;
137}
138
139uint64_t getId(const ProgramSelector& sel, const IdentifierType type, uint64_t defval) {
140 if (!hasId(sel, type)) return defval;
141 return getId(sel, type);
142}
143
144ProgramSelector make_selector(Band band, uint32_t channel, uint32_t subChannel) {
145 ProgramSelector sel = {};
146
147 ALOGW_IF((subChannel > 0) && (band == Band::AM || band == Band::FM),
148 "got subChannel for non-HD AM/FM");
149
150 // we can't use ProgramType::AM_HD or FM_HD, because we don't know HD station ID
151 ProgramType type;
152 switch (band) {
153 case Band::AM:
154 case Band::AM_HD:
155 type = ProgramType::AM;
156 break;
157 case Band::FM:
158 case Band::FM_HD:
159 type = ProgramType::FM;
160 break;
161 default:
162 LOG_ALWAYS_FATAL("Unsupported band: %s", toString(band).c_str());
163 }
164
165 sel.programType = static_cast<uint32_t>(type);
166 sel.primaryId.type = static_cast<uint32_t>(IdentifierType::AMFM_FREQUENCY);
167 sel.primaryId.value = channel;
168 if (subChannel > 0) {
169 // stating sub channel for AM/FM channel does not give any guarantees,
170 // but we can't do much more without HD station ID
171 sel.secondaryIds = hidl_vec<ProgramIdentifier>{
172 {static_cast<uint32_t>(IdentifierType::HD_SUBCHANNEL), subChannel},
173 };
174 }
175
176 return sel;
177}
178
179bool getLegacyChannel(const ProgramSelector& sel, uint32_t& channelOut, uint32_t& subChannelOut) {
180 if (isAmFm(getType(sel))) {
181 channelOut = getId(sel, IdentifierType::AMFM_FREQUENCY);
182 subChannelOut = getId(sel, IdentifierType::HD_SUBCHANNEL, 0);
183 return true;
184 } else {
185 channelOut = 0;
186 subChannelOut = 0;
187 return false;
188 }
189}
190
191bool isDigital(const ProgramSelector& sel) {
192 switch (getType(sel)) {
193 case ProgramType::AM:
194 case ProgramType::FM:
195 return false;
196 default:
197 // VENDOR might not be digital, but it doesn't matter for default impl.
198 return true;
199 }
200}
201
202} // namespace utils
203} // namespace implementation
204} // namespace V1_1
205} // namespace broadcastradio
206} // namespace hardware
207} // namespace android