Home | History | Annotate | Download | only in utils1x
      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