Implement config flags.

Bug: 69958423
Test: VTS
Change-Id: I6221d2bd2c6f1e31c93b105fce4cfc6d673e3b77
diff --git a/broadcastradio/2.0/Android.bp b/broadcastradio/2.0/Android.bp
index 5146932..afbd6d4 100644
--- a/broadcastradio/2.0/Android.bp
+++ b/broadcastradio/2.0/Android.bp
@@ -16,6 +16,7 @@
         "android.hidl.base@1.0",
     ],
     types: [
+        "ConfigFlag",
         "Constants",
         "IdentifierType",
         "Metadata",
diff --git a/broadcastradio/2.0/ITunerSession.hal b/broadcastradio/2.0/ITunerSession.hal
index ae6cbb5..8a21768 100644
--- a/broadcastradio/2.0/ITunerSession.hal
+++ b/broadcastradio/2.0/ITunerSession.hal
@@ -77,6 +77,32 @@
     cancel();
 
     /**
+     * Fetches the current setting of a given config flag.
+     *
+     * The success/failure result must be consistent with setConfigFlag.
+     *
+     * @param flag Flag to fetch.
+     * @return result OK successfully fetched the flag.
+     *                INVALID_STATE if the flag is not applicable right now.
+     *                NOT_SUPPORTED if the flag is not supported at all.
+     * @return value The current value of the flag, if result is OK.
+     */
+    getConfigFlag(ConfigFlag flag) generates (Result result, bool value);
+
+    /**
+     * Sets the config flag.
+     *
+     * The success/failure result must be consistent with getConfigFlag.
+     *
+     * @param flag Flag to set.
+     * @param value The new value of a given flag.
+     * @return result OK successfully set the flag.
+     *                INVALID_STATE if the flag is not applicable right now.
+     *                NOT_SUPPORTED if the flag is not supported at all.
+     */
+    setConfigFlag(ConfigFlag flag, bool value) generates (Result result);
+
+    /**
      * Generic method for setting vendor-specific parameter values.
      * The framework does not interpret the parameters, they are passed
      * in an opaque manner between a vendor application and HAL.
diff --git a/broadcastradio/2.0/default/TunerSession.cpp b/broadcastradio/2.0/default/TunerSession.cpp
index f0b98b8..54af3389 100644
--- a/broadcastradio/2.0/default/TunerSession.cpp
+++ b/broadcastradio/2.0/default/TunerSession.cpp
@@ -205,6 +205,19 @@
     return {};
 }
 
+Return<void> TunerSession::getConfigFlag(ConfigFlag flag, getConfigFlag_cb _hidl_cb) {
+    ALOGV("%s(%s)", __func__, toString(flag).c_str());
+
+    _hidl_cb(Result::NOT_SUPPORTED, false);
+    return {};
+}
+
+Return<Result> TunerSession::setConfigFlag(ConfigFlag flag, bool value) {
+    ALOGV("%s(%s, %d)", __func__, toString(flag).c_str(), value);
+
+    return Result::NOT_SUPPORTED;
+}
+
 Return<void> TunerSession::setParameters(const hidl_vec<VendorKeyValue>& /* parameters */,
                                          setParameters_cb _hidl_cb) {
     ALOGV("%s", __func__);
diff --git a/broadcastradio/2.0/default/TunerSession.h b/broadcastradio/2.0/default/TunerSession.h
index 08a7588..9a72182 100644
--- a/broadcastradio/2.0/default/TunerSession.h
+++ b/broadcastradio/2.0/default/TunerSession.h
@@ -38,6 +38,8 @@
     virtual Return<Result> scan(bool directionUp, bool skipSubChannel) override;
     virtual Return<Result> step(bool directionUp) override;
     virtual Return<void> cancel() override;
+    virtual Return<void> getConfigFlag(ConfigFlag flag, getConfigFlag_cb _hidl_cb);
+    virtual Return<Result> setConfigFlag(ConfigFlag flag, bool value);
     virtual Return<void> setParameters(const hidl_vec<VendorKeyValue>& parameters,
                                        setParameters_cb _hidl_cb) override;
     virtual Return<void> getParameters(const hidl_vec<hidl_string>& keys,
diff --git a/broadcastradio/2.0/types.hal b/broadcastradio/2.0/types.hal
index 4b9878b..fc5809f 100644
--- a/broadcastradio/2.0/types.hal
+++ b/broadcastradio/2.0/types.hal
@@ -37,6 +37,73 @@
 };
 
 /**
+ * Configuration flags to be used with getConfigFlag and setConfigFlag methods
+ * of ITunerSession.
+ */
+enum ConfigFlag : uint32_t {
+    /**
+     * Forces mono audio stream reception.
+     *
+     * Analog broadcasts can recover poor reception conditions by jointing
+     * stereo channels into one. Mainly for, but not limited to AM/FM.
+     */
+    FORCE_MONO = 1,
+
+    /**
+     * Forces the analog playback for the supporting radio technology.
+     *
+     * User may disable digital playback for FM HD Radio or hybrid FM/DAB with
+     * this option. This is purely user choice, ie. does not reflect digital-
+     * analog handover state managed from the HAL implementation side.
+     *
+     * Some radio technologies may not support this, ie. DAB.
+     */
+    FORCE_ANALOG,
+
+    /**
+     * Forces the digital playback for the supporting radio technology.
+     *
+     * User may disable digital-analog handover that happens with poor
+     * receiption conditions. With digital forced, the radio will remain silent
+     * instead of switching to analog channel if it's available. This is purely
+     * user choice, it does not reflect the actual state of handover.
+     */
+    FORCE_DIGITAL,
+
+    /**
+     * RDS Alternative Frequencies.
+     *
+     * If set, radio tuner automatically switches to the best available
+     * frequency that currently listened RDS station broadcasts.
+     */
+    RDS_AF,
+
+    /**
+     * RDS region-specific program lock-down.
+     *
+     * Allows user to lock to the current region as they move into the
+     * other region.
+     */
+    RDS_REG,
+
+    /**
+     * Enables DAB implicit linking, based on program identifiers
+     * (DAB SId, RDS PI).
+     */
+    DAB_IMPLICIT_LINKING,
+
+    /**
+     * Enables DAB hard linking (the same content).
+     */
+    DAB_HARD_LINKING,
+
+    /**
+     * Enables DAB hard linking (related content).
+     */
+    DAB_SOFT_LINKING,
+};
+
+/**
  * A key-value pair for vendor-specific information to be passed as-is through
  * Android framework to the front-end application.
  */
diff --git a/broadcastradio/2.0/vts/functional/VtsHalBroadcastradioV2_0TargetTest.cpp b/broadcastradio/2.0/vts/functional/VtsHalBroadcastradioV2_0TargetTest.cpp
index a12afd6..d0e4144 100644
--- a/broadcastradio/2.0/vts/functional/VtsHalBroadcastradioV2_0TargetTest.cpp
+++ b/broadcastradio/2.0/vts/functional/VtsHalBroadcastradioV2_0TargetTest.cpp
@@ -57,6 +57,12 @@
 
 }  // namespace timeout
 
+static const ConfigFlag gConfigFlagValues[] = {
+    ConfigFlag::FORCE_MONO,       ConfigFlag::FORCE_ANALOG,     ConfigFlag::FORCE_DIGITAL,
+    ConfigFlag::RDS_AF,           ConfigFlag::RDS_REG,          ConfigFlag::DAB_IMPLICIT_LINKING,
+    ConfigFlag::DAB_HARD_LINKING, ConfigFlag::DAB_SOFT_LINKING,
+};
+
 struct TunerCallbackMock : public ITunerCallback {
     TunerCallbackMock() {
         // we expect the antenna is connected through the whole test
@@ -378,6 +384,85 @@
     ASSERT_EQ(0u, len);
 }
 
+/**
+ * Test getting config flags.
+ *
+ * Verifies that:
+ * - getConfigFlag either succeeds or ends with NOT_SUPPORTED or INVALID_STATE;
+ * - call success or failure is consistent with setConfigFlag.
+ */
+TEST_F(BroadcastRadioHalTest, GetConfigFlags) {
+    ASSERT_TRUE(openSession());
+
+    for (auto flag : gConfigFlagValues) {
+        auto halResult = Result::UNKNOWN_ERROR;
+        auto cb = [&](Result result, bool) { halResult = result; };
+        auto hidlResult = mSession->getConfigFlag(flag, cb);
+        EXPECT_TRUE(hidlResult.isOk());
+
+        if (halResult != Result::NOT_SUPPORTED && halResult != Result::INVALID_STATE) {
+            ASSERT_EQ(Result::OK, halResult);
+        }
+
+        // set must fail or succeed the same way as get
+        auto setResult = mSession->setConfigFlag(flag, false);
+        EXPECT_EQ(halResult, setResult);
+        setResult = mSession->setConfigFlag(flag, true);
+        EXPECT_EQ(halResult, setResult);
+    }
+}
+
+/**
+ * Test setting config flags.
+ *
+ * Verifies that:
+ * - setConfigFlag either succeeds or ends with NOT_SUPPORTED or INVALID_STATE;
+ * - getConfigFlag reflects the state requested immediately after the set call.
+ */
+TEST_F(BroadcastRadioHalTest, SetConfigFlags) {
+    ASSERT_TRUE(openSession());
+
+    auto get = [&](ConfigFlag flag) {
+        auto halResult = Result::UNKNOWN_ERROR;
+        bool gotValue = false;
+        auto cb = [&](Result result, bool value) {
+            halResult = result;
+            gotValue = value;
+        };
+        auto hidlResult = mSession->getConfigFlag(flag, cb);
+        EXPECT_TRUE(hidlResult.isOk());
+        EXPECT_EQ(Result::OK, halResult);
+        return gotValue;
+    };
+
+    for (auto flag : gConfigFlagValues) {
+        auto result = mSession->setConfigFlag(flag, false);
+        if (result == Result::NOT_SUPPORTED || result == Result::INVALID_STATE) {
+            // setting to true must result in the same error as false
+            auto secondResult = mSession->setConfigFlag(flag, true);
+            EXPECT_EQ(result, secondResult);
+            continue;
+        }
+        ASSERT_EQ(Result::OK, result);
+
+        // verify false is set
+        auto value = get(flag);
+        EXPECT_FALSE(value);
+
+        // try setting true this time
+        result = mSession->setConfigFlag(flag, true);
+        ASSERT_EQ(Result::OK, result);
+        value = get(flag);
+        EXPECT_TRUE(value);
+
+        // false again
+        result = mSession->setConfigFlag(flag, false);
+        ASSERT_EQ(Result::OK, result);
+        value = get(flag);
+        EXPECT_FALSE(value);
+    }
+}
+
 }  // namespace vts
 }  // namespace V2_0
 }  // namespace broadcastradio