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 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 return getId(a, type) == getId(b, type); 63 } 64 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 103 ProgramType getType(const ProgramSelector& sel) { 104 return static_cast<ProgramType>(sel.programType); 105 } 106 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 119 bool isAm(const Band band) { 120 return band == Band::AM || band == Band::AM_HD; 121 } 122 123 bool isFm(const Band band) { 124 return band == Band::FM || band == Band::FM_HD; 125 } 126 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 146 bool hasId(const ProgramSelector& sel, const IdentifierType type) { 147 return maybeGetId(sel, type, nullptr); 148 } 149 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 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 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 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 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 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