Implement ProgramSelector at HIDL layer.

No front-end implementation yet.

Bug: b/32621193
Test: VTS
Change-Id: I48f034e709254836cad35bbeb4285c3c42a9e1cd
diff --git a/broadcastradio/1.1/default/Tuner.cpp b/broadcastradio/1.1/default/Tuner.cpp
index 9b39d36..bb07f36 100644
--- a/broadcastradio/1.1/default/Tuner.cpp
+++ b/broadcastradio/1.1/default/Tuner.cpp
@@ -20,6 +20,7 @@
 #include "BroadcastRadio.h"
 #include "Tuner.h"
 
+#include <Utils.h>
 #include <log/log.h>
 
 namespace android {
@@ -70,6 +71,8 @@
 
         mAmfmConfig = move(config);
         mAmfmConfig.antennaConnected = true;
+        mCurrentProgram = utils::make_selector(mAmfmConfig.type, mAmfmConfig.lowerLimit);
+
         mIsAmfmConfigSet = true;
         mCallback->configChange(Result::OK, mAmfmConfig);
     };
@@ -90,28 +93,31 @@
     return Void();
 }
 
-// makes ProgramInfo that points to no channel
-static ProgramInfo makeDummyProgramInfo(uint32_t channel) {
+// makes ProgramInfo that points to no program
+static ProgramInfo makeDummyProgramInfo(const ProgramSelector& selector) {
     ProgramInfo info11 = {};
     auto& info10 = info11.base;
 
-    info10.channel = channel;
+    utils::getLegacyChannel(selector, info10.channel, info10.subChannel);
+    info11.selector = selector;
     info11.flags |= ProgramInfoFlags::MUTED;
 
     return info11;
 }
 
-void Tuner::tuneInternalLocked() {
+void Tuner::tuneInternalLocked(const ProgramSelector& sel) {
     VirtualRadio* virtualRadio = nullptr;
     if (mAmfmConfig.type == Band::FM_HD || mAmfmConfig.type == Band::FM) {
         virtualRadio = &mVirtualFm;
     }
 
     VirtualProgram virtualProgram;
-    if (virtualRadio != nullptr && virtualRadio->getProgram(mCurrentProgram, virtualProgram)) {
+    if (virtualRadio != nullptr && virtualRadio->getProgram(sel, virtualProgram)) {
+        mCurrentProgram = virtualProgram.selector;
         mCurrentProgramInfo = static_cast<ProgramInfo>(virtualProgram);
     } else {
-        mCurrentProgramInfo = makeDummyProgramInfo(mCurrentProgram);
+        mCurrentProgram = sel;
+        mCurrentProgramInfo = makeDummyProgramInfo(sel);
     }
     mIsTuneCompleted = true;
 
@@ -152,7 +158,7 @@
     auto found = lower_bound(list.begin(), list.end(), VirtualProgram({current}));
     if (direction == Direction::UP) {
         if (found < list.end() - 1) {
-            if (found->channel == current) found++;
+            if (utils::tunesTo(current, found->selector)) found++;
         } else {
             found = list.begin();
         }
@@ -163,25 +169,31 @@
             found = list.end() - 1;
         }
     }
-    auto tuneTo = found->channel;
+    auto tuneTo = found->selector;
 
     mIsTuneCompleted = false;
     auto task = [this, tuneTo, direction]() {
         ALOGI("Performing scan %s", toString(direction).c_str());
 
         lock_guard<mutex> lk(mMut);
-        mCurrentProgram = tuneTo;
-        tuneInternalLocked();
+        tuneInternalLocked(tuneTo);
     };
     mThread.schedule(task, gDefaultDelay.scan);
 
     return Result::OK;
 }
 
-Return<Result> Tuner::step(Direction direction, bool skipSubChannel __unused) {
+Return<Result> Tuner::step(Direction direction, bool skipSubChannel) {
     ALOGV("%s", __func__);
+    ALOGW_IF(!skipSubChannel, "can't step to next frequency without ignoring subChannel");
 
     lock_guard<mutex> lk(mMut);
+
+    if (!utils::isAmFm(utils::getType(mCurrentProgram))) {
+        ALOGE("Can't step in anything else than AM/FM");
+        return Result::NOT_INITIALIZED;
+    }
+
     ALOGW_IF(!mIsAmfmConfigSet, "AM/FM config not set");
     if (!mIsAmfmConfigSet) return Result::INVALID_STATE;
     mIsTuneCompleted = false;
@@ -191,57 +203,73 @@
 
         lock_guard<mutex> lk(mMut);
 
+        auto current = utils::getId(mCurrentProgram, IdentifierType::AMFM_FREQUENCY, 0);
+
         if (direction == Direction::UP) {
-            mCurrentProgram += mAmfmConfig.spacings[0];
+            current += mAmfmConfig.spacings[0];
         } else {
-            mCurrentProgram -= mAmfmConfig.spacings[0];
+            current -= mAmfmConfig.spacings[0];
         }
 
-        if (mCurrentProgram > mAmfmConfig.upperLimit) mCurrentProgram = mAmfmConfig.lowerLimit;
-        if (mCurrentProgram < mAmfmConfig.lowerLimit) mCurrentProgram = mAmfmConfig.upperLimit;
+        if (current > mAmfmConfig.upperLimit) current = mAmfmConfig.lowerLimit;
+        if (current < mAmfmConfig.lowerLimit) current = mAmfmConfig.upperLimit;
 
-        tuneInternalLocked();
+        tuneInternalLocked(utils::make_selector(mAmfmConfig.type, current));
     };
     mThread.schedule(task, gDefaultDelay.step);
 
     return Result::OK;
 }
 
-Return<Result> Tuner::tune(uint32_t channel, uint32_t subChannel)  {
+Return<Result> Tuner::tune(uint32_t channel, uint32_t subChannel) {
     ALOGV("%s(%d, %d)", __func__, channel, subChannel);
+    Band band;
+    {
+        lock_guard<mutex> lk(mMut);
+        band = mAmfmConfig.type;
+    }
+    return tune_1_1(utils::make_selector(band, channel, subChannel));
+}
+
+Return<Result> Tuner::tune_1_1(const ProgramSelector& sel) {
+    ALOGV("%s(%s)", __func__, toString(sel).c_str());
 
     lock_guard<mutex> lk(mMut);
-    ALOGW_IF(!mIsAmfmConfigSet, "AM/FM config not set");
-    if (!mIsAmfmConfigSet) return Result::INVALID_STATE;
-    if (channel < mAmfmConfig.lowerLimit || channel > mAmfmConfig.upperLimit) {
-        return Result::INVALID_ARGUMENTS;
-    }
-    mIsTuneCompleted = false;
 
-    auto task = [this, channel]() {
+    if (utils::isAmFm(utils::getType(mCurrentProgram))) {
+        ALOGW_IF(!mIsAmfmConfigSet, "AM/FM config not set");
+        if (!mIsAmfmConfigSet) return Result::INVALID_STATE;
+
+        auto freq = utils::getId(sel, IdentifierType::AMFM_FREQUENCY);
+        if (freq < mAmfmConfig.lowerLimit || freq > mAmfmConfig.upperLimit) {
+            return Result::INVALID_ARGUMENTS;
+        }
+    }
+
+    mIsTuneCompleted = false;
+    auto task = [this, sel]() {
         lock_guard<mutex> lk(mMut);
-        mCurrentProgram = channel;
-        tuneInternalLocked();
+        tuneInternalLocked(sel);
     };
     mThread.schedule(task, gDefaultDelay.tune);
 
     return Result::OK;
 }
 
-Return<Result> Tuner::cancel()  {
+Return<Result> Tuner::cancel() {
     ALOGV("%s", __func__);
     mThread.cancelAll();
     return Result::OK;
 }
 
-Return<void> Tuner::getProgramInformation(getProgramInformation_cb _hidl_cb)  {
+Return<void> Tuner::getProgramInformation(getProgramInformation_cb _hidl_cb) {
     ALOGV("%s", __func__);
     return getProgramInformation_1_1([&](Result result, const ProgramInfo& info) {
         _hidl_cb(result, info.base);
     });
 }
 
-Return<void> Tuner::getProgramInformation_1_1(getProgramInformation_1_1_cb _hidl_cb)  {
+Return<void> Tuner::getProgramInformation_1_1(getProgramInformation_1_1_cb _hidl_cb) {
     ALOGV("%s", __func__);
 
     lock_guard<mutex> lk(mMut);
diff --git a/broadcastradio/1.1/default/Tuner.h b/broadcastradio/1.1/default/Tuner.h
index 7719d4d..17ee99f 100644
--- a/broadcastradio/1.1/default/Tuner.h
+++ b/broadcastradio/1.1/default/Tuner.h
@@ -39,6 +39,7 @@
     Return<Result> scan(V1_0::Direction direction, bool skipSubChannel) override;
     Return<Result> step(V1_0::Direction direction, bool skipSubChannel) override;
     Return<Result> tune(uint32_t channel, uint32_t subChannel) override;
+    Return<Result> tune_1_1(const ProgramSelector& program) override;
     Return<Result> cancel() override;
     Return<void> getProgramInformation(getProgramInformation_cb _hidl_cb) override;
     Return<void> getProgramInformation_1_1(getProgramInformation_1_1_cb _hidl_cb) override;
@@ -60,10 +61,10 @@
     bool mIsAmfmConfigSet = false;
     V1_0::BandConfig mAmfmConfig;
     bool mIsTuneCompleted = false;
-    uint32_t mCurrentProgram;  // TODO(b/32621193): Station Selector
+    ProgramSelector mCurrentProgram = {};
     ProgramInfo mCurrentProgramInfo = {};
 
-    void tuneInternalLocked();
+    void tuneInternalLocked(const ProgramSelector& sel);
 };
 
 }  // namespace implementation
diff --git a/broadcastradio/1.1/default/VirtualProgram.cpp b/broadcastradio/1.1/default/VirtualProgram.cpp
index df12a3e..ef8bd1c 100644
--- a/broadcastradio/1.1/default/VirtualProgram.cpp
+++ b/broadcastradio/1.1/default/VirtualProgram.cpp
@@ -15,6 +15,8 @@
  */
 #include "VirtualProgram.h"
 
+#include <Utils.h>
+
 namespace android {
 namespace hardware {
 namespace broadcastradio {
@@ -29,10 +31,12 @@
     ProgramInfo info11 = {};
     auto& info10 = info11.base;
 
-    info10.channel = channel;
+    utils::getLegacyChannel(selector, info10.channel, info10.subChannel);
+    info11.selector = selector;
     info10.tuned = true;
     info10.stereo = true;
-    info10.signalStrength = 100;
+    info10.digital = utils::isDigital(selector);
+    info10.signalStrength = info10.digital ? 100 : 80;
 
     info10.metadata = hidl_vec<MetaData>({
         {MetadataType::TEXT, MetadataKey::RDS_PS, {}, {}, programName, {}},
@@ -43,8 +47,25 @@
     return info11;
 }
 
+// Defining order on virtual programs, how they appear on band.
+// It's mostly for default implementation purposes, may not be complete or correct.
 bool operator<(const VirtualProgram& lhs, const VirtualProgram& rhs) {
-    return lhs.channel < rhs.channel;
+    auto& l = lhs.selector;
+    auto& r = rhs.selector;
+
+    // Two programs with the same primaryId is considered the same.
+    if (l.programType != r.programType) return l.programType < r.programType;
+    if (l.primaryId.type != r.primaryId.type) return l.primaryId.type < r.primaryId.type;
+    if (l.primaryId.value != r.primaryId.value) return l.primaryId.value < r.primaryId.value;
+
+    // A little exception for HD Radio subchannel - we check secondary ID too.
+    if (utils::hasId(l, IdentifierType::HD_SUBCHANNEL) &&
+        utils::hasId(r, IdentifierType::HD_SUBCHANNEL)) {
+        return utils::getId(l, IdentifierType::HD_SUBCHANNEL) <
+               utils::getId(r, IdentifierType::HD_SUBCHANNEL);
+    }
+
+    return false;
 }
 
 }  // namespace implementation
diff --git a/broadcastradio/1.1/default/VirtualProgram.h b/broadcastradio/1.1/default/VirtualProgram.h
index 303513f..a4fd72c 100644
--- a/broadcastradio/1.1/default/VirtualProgram.h
+++ b/broadcastradio/1.1/default/VirtualProgram.h
@@ -26,7 +26,7 @@
 namespace implementation {
 
 struct VirtualProgram {
-    uint32_t channel;  // TODO(b/32621193): Station Selector
+    ProgramSelector selector;
 
     std::string programName = "";
     std::string songArtist = "";
diff --git a/broadcastradio/1.1/default/VirtualRadio.cpp b/broadcastradio/1.1/default/VirtualRadio.cpp
index 0eab7ae..acf37a4 100644
--- a/broadcastradio/1.1/default/VirtualRadio.cpp
+++ b/broadcastradio/1.1/default/VirtualRadio.cpp
@@ -15,25 +15,31 @@
  */
 #include "VirtualRadio.h"
 
+#include <Utils.h>
+
 namespace android {
 namespace hardware {
 namespace broadcastradio {
 namespace V1_1 {
 namespace implementation {
 
+using V1_0::Band;
+
 using std::lock_guard;
 using std::move;
 using std::mutex;
 using std::vector;
 
+using utils::make_selector;
+
 vector<VirtualProgram> gInitialFmPrograms{
-    {94900, "Wild 94.9", "Drake ft. Rihanna", "Too Good"},
-    {96500, "KOIT", "Celine Dion", "All By Myself"},
-    {97300, "Alice@97.3", "Drops of Jupiter", "Train"},
-    {99700, "99.7 Now!", "The Chainsmokers", "Closer"},
-    {101300, "101-3 KISS-FM", "Justin Timberlake", "Rock Your Body"},
-    {103700, "iHeart80s @ 103.7", "Michael Jackson", "Billie Jean"},
-    {106100, "106 KMEL", "Drake", "Marvins Room"},
+    {make_selector(Band::FM, 94900), "Wild 94.9", "Drake ft. Rihanna", "Too Good"},
+    {make_selector(Band::FM, 96500), "KOIT", "Celine Dion", "All By Myself"},
+    {make_selector(Band::FM, 97300), "Alice@97.3", "Drops of Jupiter", "Train"},
+    {make_selector(Band::FM, 99700), "99.7 Now!", "The Chainsmokers", "Closer"},
+    {make_selector(Band::FM, 101300), "101-3 KISS-FM", "Justin Timberlake", "Rock Your Body"},
+    {make_selector(Band::FM, 103700), "iHeart80s @ 103.7", "Michael Jackson", "Billie Jean"},
+    {make_selector(Band::FM, 106100), "106 KMEL", "Drake", "Marvins Room"},
 };
 
 VirtualRadio::VirtualRadio(VirtualRadio&& o) : mPrograms(move(o.mPrograms)) {}
@@ -45,10 +51,10 @@
     return mPrograms;
 }
 
-bool VirtualRadio::getProgram(uint32_t channel, VirtualProgram& programOut) {
+bool VirtualRadio::getProgram(const ProgramSelector& selector, VirtualProgram& programOut) {
     lock_guard<mutex> lk(mMut);
     for (auto&& program : mPrograms) {
-        if (program.channel == channel) {
+        if (utils::tunesTo(selector, program.selector)) {
             programOut = program;
             return true;
         }
diff --git a/broadcastradio/1.1/default/VirtualRadio.h b/broadcastradio/1.1/default/VirtualRadio.h
index e1918a0..23cb06c 100644
--- a/broadcastradio/1.1/default/VirtualRadio.h
+++ b/broadcastradio/1.1/default/VirtualRadio.h
@@ -33,7 +33,7 @@
     VirtualRadio(std::vector<VirtualProgram> initialList);
 
     std::vector<VirtualProgram> getProgramList();
-    bool getProgram(uint32_t channel, VirtualProgram& program);
+    bool getProgram(const ProgramSelector& selector, VirtualProgram& program);
 
    private:
     std::mutex mMut;