Implement program list fetching.

Bug: 69860743
Test: VTS
Change-Id: I04eb43c1e0e1bb7bad86e123594a473454eed983
diff --git a/broadcastradio/common/utils2x/Utils.cpp b/broadcastradio/common/utils2x/Utils.cpp
index d157108..10a155b 100644
--- a/broadcastradio/common/utils2x/Utils.cpp
+++ b/broadcastradio/common/utils2x/Utils.cpp
@@ -18,6 +18,7 @@
 
 #include <broadcastradio-utils-2x/Utils.h>
 
+#include <android-base/logging.h>
 #include <log/log.h>
 
 namespace android {
@@ -28,14 +29,64 @@
 using V2_0::IdentifierType;
 using V2_0::Metadata;
 using V2_0::MetadataKey;
+using V2_0::ProgramFilter;
 using V2_0::ProgramIdentifier;
+using V2_0::ProgramInfo;
+using V2_0::ProgramListChunk;
 using V2_0::ProgramSelector;
+using V2_0::Properties;
 
 using std::string;
 using std::vector;
 
+IdentifierType getType(uint32_t typeAsInt) {
+    return static_cast<IdentifierType>(typeAsInt);
+}
+
 IdentifierType getType(const ProgramIdentifier& id) {
-    return static_cast<IdentifierType>(id.type);
+    return getType(id.type);
+}
+
+IdentifierIterator::IdentifierIterator(const V2_0::ProgramSelector& sel)
+    : IdentifierIterator(sel, 0) {}
+
+IdentifierIterator::IdentifierIterator(const V2_0::ProgramSelector& sel, size_t pos)
+    : mSel(sel), mPos(pos) {}
+
+IdentifierIterator IdentifierIterator::operator++(int) {
+    auto i = *this;
+    mPos++;
+    return i;
+}
+
+IdentifierIterator& IdentifierIterator::operator++() {
+    ++mPos;
+    return *this;
+}
+
+IdentifierIterator::ref_type IdentifierIterator::operator*() const {
+    if (mPos == 0) return sel().primaryId;
+
+    // mPos is 1-based for secondary identifiers
+    DCHECK(mPos <= sel().secondaryIds.size());
+    return sel().secondaryIds[mPos - 1];
+}
+
+bool IdentifierIterator::operator==(const IdentifierIterator& rhs) const {
+    // Check, if both iterators points at the same selector.
+    if (reinterpret_cast<uintptr_t>(&sel()) != reinterpret_cast<uintptr_t>(&rhs.sel())) {
+        return false;
+    }
+
+    return mPos == rhs.mPos;
+}
+
+IdentifierIterator begin(const V2_0::ProgramSelector& sel) {
+    return IdentifierIterator(sel);
+}
+
+IdentifierIterator end(const V2_0::ProgramSelector& sel) {
+    return IdentifierIterator(sel) + 1 /* primary id */ + sel.secondaryIds.size();
 }
 
 static bool bothHaveId(const ProgramSelector& a, const ProgramSelector& b,
@@ -88,6 +139,7 @@
         return true;
     }
 
+    // TODO(twasilczyk): use IdentifierIterator
     // not optimal, but we don't care in default impl
     for (auto&& id : sel.secondaryIds) {
         if (id.type == itype) {
@@ -125,6 +177,7 @@
 
     if (sel.primaryId.type == itype) ret.push_back(sel.primaryId.value);
 
+    // TODO(twasilczyk): use IdentifierIterator
     for (auto&& id : sel.secondaryIds) {
         if (id.type == itype) ret.push_back(id.value);
     }
@@ -132,11 +185,11 @@
     return ret;
 }
 
-bool isSupported(const V2_0::Properties& prop, const V2_0::ProgramSelector& sel) {
+bool isSupported(const Properties& prop, const ProgramSelector& sel) {
+    // TODO(twasilczyk): use IdentifierIterator
     // Not optimal, but it doesn't matter for default impl nor VTS tests.
-    for (auto&& idTypeI : prop.supportedIdentifierTypes) {
-        auto idType = static_cast<IdentifierType>(idTypeI);
-        if (hasId(sel, idType)) return true;
+    for (auto&& idType : prop.supportedIdentifierTypes) {
+        if (hasId(sel, getType(idType))) return true;
     }
     return false;
 }
@@ -152,7 +205,7 @@
         }
     };
 
-    switch (static_cast<IdentifierType>(id.type)) {
+    switch (getType(id)) {
         case IdentifierType::AMFM_FREQUENCY:
         case IdentifierType::DAB_FREQUENCY:
         case IdentifierType::DRMO_FREQUENCY:
@@ -211,8 +264,9 @@
     return valid;
 }
 
-bool isValid(const V2_0::ProgramSelector& sel) {
+bool isValid(const ProgramSelector& sel) {
     if (!isValid(sel.primaryId)) return false;
+    // TODO(twasilczyk): use IdentifierIterator
     for (auto&& id : sel.secondaryIds) {
         if (!isValid(id)) return false;
     }
@@ -243,6 +297,59 @@
     return meta;
 }
 
+bool satisfies(const ProgramFilter& filter, const ProgramSelector& sel) {
+    if (filter.identifierTypes.size() > 0) {
+        auto typeEquals = [](const V2_0::ProgramIdentifier& id, uint32_t type) {
+            return id.type == type;
+        };
+        auto it = std::find_first_of(begin(sel), end(sel), filter.identifierTypes.begin(),
+                                     filter.identifierTypes.end(), typeEquals);
+        if (it == end(sel)) return false;
+    }
+
+    if (filter.identifiers.size() > 0) {
+        auto it = std::find_first_of(begin(sel), end(sel), filter.identifiers.begin(),
+                                     filter.identifiers.end());
+        if (it == end(sel)) return false;
+    }
+
+    if (!filter.includeCategories) {
+        if (getType(sel.primaryId) == IdentifierType::DAB_ENSEMBLE) return false;
+    }
+
+    return true;
+}
+
+size_t ProgramInfoHasher::operator()(const ProgramInfo& info) const {
+    auto& id = info.selector.primaryId;
+
+    /* This is not the best hash implementation, but good enough for default HAL
+     * implementation and tests. */
+    auto h = std::hash<uint32_t>{}(id.type);
+    h += 0x9e3779b9;
+    h ^= std::hash<uint64_t>{}(id.value);
+
+    return h;
+}
+
+bool ProgramInfoKeyEqual::operator()(const ProgramInfo& info1, const ProgramInfo& info2) const {
+    auto& id1 = info1.selector.primaryId;
+    auto& id2 = info2.selector.primaryId;
+    return id1.type == id2.type && id1.value == id2.value;
+}
+
+void updateProgramList(ProgramInfoSet& list, const ProgramListChunk& chunk) {
+    if (chunk.purge) list.clear();
+
+    list.insert(chunk.modified.begin(), chunk.modified.end());
+
+    for (auto&& id : chunk.removed) {
+        ProgramInfo info = {};
+        info.selector.primaryId = id;
+        list.erase(info);
+    }
+}
+
 }  // namespace utils
 }  // namespace broadcastradio
 }  // namespace hardware
diff --git a/broadcastradio/common/utils2x/include/broadcastradio-utils-2x/Utils.h b/broadcastradio/common/utils2x/include/broadcastradio-utils-2x/Utils.h
index dd01852..bac11fd 100644
--- a/broadcastradio/common/utils2x/include/broadcastradio-utils-2x/Utils.h
+++ b/broadcastradio/common/utils2x/include/broadcastradio-utils-2x/Utils.h
@@ -20,14 +20,49 @@
 #include <chrono>
 #include <queue>
 #include <thread>
+#include <unordered_set>
 
 namespace android {
 namespace hardware {
 namespace broadcastradio {
 namespace utils {
 
+V2_0::IdentifierType getType(uint32_t typeAsInt);
 V2_0::IdentifierType getType(const V2_0::ProgramIdentifier& id);
 
+class IdentifierIterator
+    : public std::iterator<std::random_access_iterator_tag, V2_0::ProgramIdentifier, ssize_t,
+                           const V2_0::ProgramIdentifier*, const V2_0::ProgramIdentifier&> {
+    using traits = std::iterator_traits<IdentifierIterator>;
+    using ptr_type = typename traits::pointer;
+    using ref_type = typename traits::reference;
+    using diff_type = typename traits::difference_type;
+
+   public:
+    explicit IdentifierIterator(const V2_0::ProgramSelector& sel);
+
+    IdentifierIterator operator++(int);
+    IdentifierIterator& operator++();
+    ref_type operator*() const;
+    inline ptr_type operator->() const { return &operator*(); }
+    IdentifierIterator operator+(diff_type v) const { return IdentifierIterator(mSel, mPos + v); }
+    bool operator==(const IdentifierIterator& rhs) const;
+    inline bool operator!=(const IdentifierIterator& rhs) const { return !operator==(rhs); };
+
+   private:
+    explicit IdentifierIterator(const V2_0::ProgramSelector& sel, size_t pos);
+
+    std::reference_wrapper<const V2_0::ProgramSelector> mSel;
+
+    const V2_0::ProgramSelector& sel() const { return mSel.get(); }
+
+    /** 0 is the primary identifier, 1-n are secondary identifiers. */
+    size_t mPos = 0;
+};
+
+IdentifierIterator begin(const V2_0::ProgramSelector& sel);
+IdentifierIterator end(const V2_0::ProgramSelector& sel);
+
 /**
  * Checks, if {@code pointer} tunes to {@channel}.
  *
@@ -77,6 +112,21 @@
 V2_0::Metadata make_metadata(V2_0::MetadataKey key, int64_t value);
 V2_0::Metadata make_metadata(V2_0::MetadataKey key, std::string value);
 
+bool satisfies(const V2_0::ProgramFilter& filter, const V2_0::ProgramSelector& sel);
+
+struct ProgramInfoHasher {
+    size_t operator()(const V2_0::ProgramInfo& info) const;
+};
+
+struct ProgramInfoKeyEqual {
+    bool operator()(const V2_0::ProgramInfo& info1, const V2_0::ProgramInfo& info2) const;
+};
+
+typedef std::unordered_set<V2_0::ProgramInfo, ProgramInfoHasher, ProgramInfoKeyEqual>
+    ProgramInfoSet;
+
+void updateProgramList(ProgramInfoSet& list, const V2_0::ProgramListChunk& chunk);
+
 }  // namespace utils
 }  // namespace broadcastradio
 }  // namespace hardware