Initial Broadcast Radio HAL 1.1 default implementation.
The new implementation does not rely on legacy HAL, it implements
HAL 1.1 directly instead.
This is not a full implementation yet, but passes all existing tests.
Bug: b/36864090
Test: VTS, instrumentalization, Unit
Change-Id: Ic8eddd19d0bf6777b4086d099183d71e05d7da0e
diff --git a/broadcastradio/1.1/default/Android.bp b/broadcastradio/1.1/default/Android.bp
index 759eb09..bec8524 100644
--- a/broadcastradio/1.1/default/Android.bp
+++ b/broadcastradio/1.1/default/Android.bp
@@ -27,16 +27,18 @@
"BroadcastRadio.cpp",
"BroadcastRadioFactory.cpp",
"Tuner.cpp",
- "Utils.cpp",
+ "VirtualProgram.cpp",
+ "VirtualRadio.cpp",
+ ],
+ static_libs: [
+ "android.hardware.broadcastradio@1.1-utils-lib",
],
shared_libs: [
"libhidlbase",
"libhidltransport",
"libutils",
"liblog",
- "libhardware",
"android.hardware.broadcastradio@1.0",
"android.hardware.broadcastradio@1.1",
- "libradio_metadata",
],
}
diff --git a/broadcastradio/1.1/default/BroadcastRadio.cpp b/broadcastradio/1.1/default/BroadcastRadio.cpp
index d65fe6d..3aac127 100644
--- a/broadcastradio/1.1/default/BroadcastRadio.cpp
+++ b/broadcastradio/1.1/default/BroadcastRadio.cpp
@@ -13,14 +13,12 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-#define LOG_TAG "BroadcastRadio"
-//#define LOG_NDEBUG 0
-
-#include <log/log.h>
+#define LOG_TAG "BroadcastRadioDefault.module"
+#define LOG_NDEBUG 0
#include "BroadcastRadio.h"
-#include "Tuner.h"
-#include "Utils.h"
+
+#include <log/log.h>
namespace android {
namespace hardware {
@@ -28,125 +26,126 @@
namespace V1_1 {
namespace implementation {
-using ::android::sp;
+using V1_0::Band;
+using V1_0::BandConfig;
+using V1_0::Class;
+using V1_0::Deemphasis;
+using V1_0::Rds;
+
+using std::lock_guard;
+using std::map;
+using std::mutex;
+using std::vector;
+
+// clang-format off
+static const map<Class, ModuleConfig> gModuleConfigs{
+ {Class::AM_FM, ModuleConfig({
+ "Digital radio mock",
+ { // amFmBands
+ AmFmBandConfig({
+ Band::AM_HD,
+ 540, // lowerLimit
+ 1610, // upperLimit
+ 10, // spacing
+ }),
+ AmFmBandConfig({
+ Band::FM_HD,
+ 87900, // lowerLimit
+ 107900, // upperLimit
+ 200, // spacing
+ }),
+ },
+ })},
+
+ {Class::SAT, ModuleConfig({
+ "Satellite radio mock",
+ {}, // amFmBands
+ })},
+};
+// clang-format on
BroadcastRadio::BroadcastRadio(Class classId)
- : mStatus(Result::NOT_INITIALIZED), mClassId(classId), mHwDevice(NULL)
-{
+ : mClassId(classId), mConfig(gModuleConfigs.at(classId)) {}
+
+bool BroadcastRadio::isSupported(Class classId) {
+ return gModuleConfigs.find(classId) != gModuleConfigs.end();
}
-BroadcastRadio::~BroadcastRadio()
-{
- if (mHwDevice != NULL) {
- radio_hw_device_close(mHwDevice);
- }
-}
-
-void BroadcastRadio::onFirstRef()
-{
- const hw_module_t *mod;
- int rc;
- ALOGI("%s mClassId %d", __FUNCTION__, mClassId);
-
- mHwDevice = NULL;
- const char *classString = Utils::getClassString(mClassId);
- if (classString == NULL) {
- ALOGE("invalid class ID %d", mClassId);
- mStatus = Result::INVALID_ARGUMENTS;
- return;
- }
-
- ALOGI("%s RADIO_HARDWARE_MODULE_ID %s %s",
- __FUNCTION__, RADIO_HARDWARE_MODULE_ID, classString);
-
- rc = hw_get_module_by_class(RADIO_HARDWARE_MODULE_ID, classString, &mod);
- if (rc != 0) {
- ALOGE("couldn't load radio module %s.%s (%s)",
- RADIO_HARDWARE_MODULE_ID, classString, strerror(-rc));
- return;
- }
- rc = radio_hw_device_open(mod, &mHwDevice);
- if (rc != 0) {
- ALOGE("couldn't open radio hw device in %s.%s (%s)",
- RADIO_HARDWARE_MODULE_ID, "primary", strerror(-rc));
- mHwDevice = NULL;
- return;
- }
- if (mHwDevice->common.version != RADIO_DEVICE_API_VERSION_CURRENT) {
- ALOGE("wrong radio hw device version %04x", mHwDevice->common.version);
- radio_hw_device_close(mHwDevice);
- mHwDevice = NULL;
- } else {
- mStatus = Result::OK;
- }
-}
-
-int BroadcastRadio::closeHalTuner(const struct radio_tuner *halTuner)
-{
- ALOGV("%s", __FUNCTION__);
- if (mHwDevice == NULL) {
- return -ENODEV;
- }
- if (halTuner == 0) {
- return -EINVAL;
- }
- return mHwDevice->close_tuner(mHwDevice, halTuner);
-}
-
-
-// Methods from ::android::hardware::broadcastradio::V1_1::IBroadcastRadio follow.
-Return<void> BroadcastRadio::getProperties(getProperties_cb _hidl_cb)
-{
- int rc;
- radio_hal_properties_t halProperties;
- Properties properties;
-
- if (mHwDevice == NULL) {
- rc = -ENODEV;
- goto exit;
- }
- rc = mHwDevice->get_properties(mHwDevice, &halProperties);
- if (rc == 0) {
- Utils::convertPropertiesFromHal(&properties, &halProperties);
- }
-
-exit:
- _hidl_cb(Utils::convertHalResult(rc), properties);
- return Void();
+Return<void> BroadcastRadio::getProperties(getProperties_cb _hidl_cb) {
+ ALOGV("%s", __func__);
+ return getProperties_1_1(
+ [&](const Properties& properties) { _hidl_cb(Result::OK, properties.base); });
}
Return<void> BroadcastRadio::getProperties_1_1(getProperties_1_1_cb _hidl_cb) {
- radio_hal_properties_t halProperties;
- V1_1::Properties properties = {};
+ ALOGV("%s", __func__);
+ Properties prop11 = {};
+ auto& prop10 = prop11.base;
- LOG_ALWAYS_FATAL_IF(mHwDevice == nullptr, "HW device is not set");
- int rc = mHwDevice->get_properties(mHwDevice, &halProperties);
- LOG_ALWAYS_FATAL_IF(rc != 0, "Couldn't get device properties");
- Utils::convertPropertiesFromHal(&properties.base, &halProperties);
+ prop10.classId = mClassId;
+ prop10.implementor = "Google";
+ prop10.product = mConfig.productName;
+ prop10.numTuners = 1;
+ prop10.numAudioSources = 1;
+ prop10.supportsCapture = false;
+ prop11.supportsBackgroundScanning = false;
+ prop11.vendorExension = "dummy";
- _hidl_cb(properties);
- return Void();
-}
+ prop10.bands.resize(mConfig.amFmBands.size());
+ for (size_t i = 0; i < mConfig.amFmBands.size(); i++) {
+ auto& src = mConfig.amFmBands[i];
+ auto& dst = prop10.bands[i];
-Return<void> BroadcastRadio::openTuner(const BandConfig& config, bool audio,
- const sp<V1_0::ITunerCallback>& callback, openTuner_cb _hidl_cb)
-{
- sp<Tuner> tunerImpl = new Tuner(callback, this);
+ dst.type = src.type;
+ dst.antennaConnected = true;
+ dst.lowerLimit = src.lowerLimit;
+ dst.upperLimit = src.upperLimit;
+ dst.spacings = vector<uint32_t>({src.spacing});
- radio_hal_band_config_t halConfig;
- const struct radio_tuner *halTuner;
- Utils::convertBandConfigToHal(&halConfig, &config);
- int rc = mHwDevice->open_tuner(mHwDevice, &halConfig, audio, Tuner::callback,
- tunerImpl.get(), &halTuner);
- if (rc == 0) {
- tunerImpl->setHalTuner(halTuner);
+ if (src.type == Band::AM) {
+ dst.ext.am.stereo = true;
+ } else if (src.type == Band::FM) {
+ dst.ext.fm.deemphasis = Deemphasis::D75;
+ dst.ext.fm.stereo = true;
+ dst.ext.fm.rds = Rds::US;
+ dst.ext.fm.ta = true;
+ dst.ext.fm.af = true;
+ dst.ext.fm.ea = true;
+ }
}
- _hidl_cb(Utils::convertHalResult(rc), tunerImpl);
+ _hidl_cb(prop11);
return Void();
}
-} // namespace implementation
+Return<void> BroadcastRadio::openTuner(const BandConfig& config, bool audio __unused,
+ const sp<V1_0::ITunerCallback>& callback,
+ openTuner_cb _hidl_cb) {
+ ALOGV("%s", __func__);
+ lock_guard<mutex> lk(mMut);
+
+ auto oldTuner = mTuner.promote();
+ if (oldTuner != nullptr) {
+ ALOGI("Force-closing previously opened tuner");
+ oldTuner->forceClose();
+ mTuner = nullptr;
+ }
+
+ sp<Tuner> newTuner = new Tuner(callback);
+ mTuner = newTuner;
+ if (mClassId == Class::AM_FM) {
+ auto ret = newTuner->setConfiguration(config);
+ if (ret != Result::OK) {
+ _hidl_cb(Result::INVALID_ARGUMENTS, {});
+ return Void();
+ }
+ }
+
+ _hidl_cb(Result::OK, newTuner);
+ return Void();
+}
+
+} // namespace implementation
} // namespace V1_1
} // namespace broadcastradio
} // namespace hardware
diff --git a/broadcastradio/1.1/default/BroadcastRadio.h b/broadcastradio/1.1/default/BroadcastRadio.h
index 7de31a0..d98afa7 100644
--- a/broadcastradio/1.1/default/BroadcastRadio.h
+++ b/broadcastradio/1.1/default/BroadcastRadio.h
@@ -16,9 +16,10 @@
#ifndef ANDROID_HARDWARE_BROADCASTRADIO_V1_1_BROADCASTRADIO_H
#define ANDROID_HARDWARE_BROADCASTRADIO_V1_1_BROADCASTRADIO_H
+#include "Tuner.h"
+
#include <android/hardware/broadcastradio/1.1/IBroadcastRadio.h>
#include <android/hardware/broadcastradio/1.1/types.h>
-#include <hardware/radio.h>
namespace android {
namespace hardware {
@@ -26,42 +27,48 @@
namespace V1_1 {
namespace implementation {
-using V1_0::Class;
-using V1_0::BandConfig;
-using V1_0::Properties;
+struct AmFmBandConfig {
+ V1_0::Band type;
+ uint32_t lowerLimit; // kHz
+ uint32_t upperLimit; // kHz
+ uint32_t spacing; // kHz
+};
+
+struct ModuleConfig {
+ std::string productName;
+ std::vector<AmFmBandConfig> amFmBands;
+};
struct BroadcastRadio : public V1_1::IBroadcastRadio {
+ /**
+ * Constructs new broadcast radio module.
+ *
+ * Before calling a constructor with a given classId, it must be checked with isSupported
+ * method first. Otherwise it results in undefined behaviour.
+ *
+ * @param classId type of a radio.
+ */
+ BroadcastRadio(V1_0::Class classId);
- BroadcastRadio(Class classId);
+ /**
+ * Checks, if a given radio type is supported.
+ *
+ * @param classId type of a radio.
+ */
+ static bool isSupported(V1_0::Class classId);
- // Methods from ::android::hardware::broadcastradio::V1_1::IBroadcastRadio follow.
+ // V1_1::IBroadcastRadio methods
Return<void> getProperties(getProperties_cb _hidl_cb) override;
Return<void> getProperties_1_1(getProperties_1_1_cb _hidl_cb) override;
- Return<void> openTuner(const BandConfig& config, bool audio,
- const sp<V1_0::ITunerCallback>& callback, openTuner_cb _hidl_cb) override;
+ Return<void> openTuner(const V1_0::BandConfig& config, bool audio,
+ const sp<V1_0::ITunerCallback>& callback,
+ openTuner_cb _hidl_cb) override;
- // RefBase
- virtual void onFirstRef() override;
-
- Result initCheck() { return mStatus; }
- int closeHalTuner(const struct radio_tuner *halTuner);
-
-private:
- virtual ~BroadcastRadio();
-
- static const char * sClassModuleNames[];
-
- Result convertHalResult(int rc);
- void convertBandConfigFromHal(BandConfig *config,
- const radio_hal_band_config_t *halConfig);
- void convertPropertiesFromHal(Properties *properties,
- const radio_hal_properties_t *halProperties);
- void convertBandConfigToHal(radio_hal_band_config_t *halConfig,
- const BandConfig *config);
-
- Result mStatus;
- Class mClassId;
- struct radio_hw_device *mHwDevice;
+ private:
+ std::mutex mMut;
+ V1_0::Class mClassId;
+ ModuleConfig mConfig;
+ wp<Tuner> mTuner;
};
} // namespace implementation
diff --git a/broadcastradio/1.1/default/BroadcastRadioFactory.cpp b/broadcastradio/1.1/default/BroadcastRadioFactory.cpp
index c8b6c39..c2c1158 100644
--- a/broadcastradio/1.1/default/BroadcastRadioFactory.cpp
+++ b/broadcastradio/1.1/default/BroadcastRadioFactory.cpp
@@ -13,29 +13,51 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+#define LOG_TAG "BroadcastRadioDefault.factory"
+#define LOG_NDEBUG 0
+
#include "BroadcastRadioFactory.h"
+
#include "BroadcastRadio.h"
+#include <log/log.h>
+
namespace android {
namespace hardware {
namespace broadcastradio {
namespace V1_1 {
namespace implementation {
-// Methods from ::android::hardware::broadcastradio::V1_0::IBroadcastRadioFactory follow.
-Return<void> BroadcastRadioFactory::connectModule(Class classId, connectModule_cb _hidl_cb) {
- sp<BroadcastRadio> impl = new BroadcastRadio(classId);
- Result retval = Result::NOT_INITIALIZED;
- if (impl != 0) {
- retval = impl->initCheck();
- }
- _hidl_cb(retval, impl);
- return Void();
+using V1_0::Class;
+
+using std::vector;
+
+static const vector<Class> gAllClasses = {
+ Class::AM_FM, Class::SAT, Class::DT,
+};
+
+IBroadcastRadioFactory* HIDL_FETCH_IBroadcastRadioFactory(const char* name __unused) {
+ return new BroadcastRadioFactory();
}
+BroadcastRadioFactory::BroadcastRadioFactory() {
+ for (auto&& classId : gAllClasses) {
+ if (!BroadcastRadio::isSupported(classId)) continue;
+ mRadioModules[classId] = new BroadcastRadio(classId);
+ }
+}
-IBroadcastRadioFactory* HIDL_FETCH_IBroadcastRadioFactory(const char* /* name */) {
- return new BroadcastRadioFactory();
+Return<void> BroadcastRadioFactory::connectModule(Class classId, connectModule_cb _hidl_cb) {
+ ALOGV("%s", __func__);
+
+ auto moduleIt = mRadioModules.find(classId);
+ if (moduleIt == mRadioModules.end()) {
+ _hidl_cb(Result::INVALID_ARGUMENTS, nullptr);
+ } else {
+ _hidl_cb(Result::OK, moduleIt->second);
+ }
+
+ return Void();
}
} // namespace implementation
diff --git a/broadcastradio/1.1/default/BroadcastRadioFactory.h b/broadcastradio/1.1/default/BroadcastRadioFactory.h
index 8eb8514..8b67ac3 100644
--- a/broadcastradio/1.1/default/BroadcastRadioFactory.h
+++ b/broadcastradio/1.1/default/BroadcastRadioFactory.h
@@ -16,6 +16,7 @@
#ifndef ANDROID_HARDWARE_BROADCASTRADIO_V1_1_BROADCASTRADIOFACTORY_H
#define ANDROID_HARDWARE_BROADCASTRADIO_V1_1_BROADCASTRADIOFACTORY_H
+#include <android/hardware/broadcastradio/1.1/IBroadcastRadio.h>
#include <android/hardware/broadcastradio/1.1/IBroadcastRadioFactory.h>
#include <android/hardware/broadcastradio/1.1/types.h>
@@ -25,14 +26,17 @@
namespace V1_1 {
namespace implementation {
-using V1_0::Class;
+extern "C" IBroadcastRadioFactory* HIDL_FETCH_IBroadcastRadioFactory(const char* name);
struct BroadcastRadioFactory : public IBroadcastRadioFactory {
- // Methods from ::android::hardware::broadcastradio::V1_0::IBroadcastRadioFactory follow.
- Return<void> connectModule(Class classId, connectModule_cb _hidl_cb) override;
-};
+ BroadcastRadioFactory();
-extern "C" IBroadcastRadioFactory* HIDL_FETCH_IBroadcastRadioFactory(const char* name);
+ // V1_0::IBroadcastRadioFactory methods
+ Return<void> connectModule(V1_0::Class classId, connectModule_cb _hidl_cb) override;
+
+ private:
+ std::map<V1_0::Class, sp<IBroadcastRadio>> mRadioModules;
+};
} // namespace implementation
} // namespace V1_1
diff --git a/broadcastradio/1.1/default/Tuner.cpp b/broadcastradio/1.1/default/Tuner.cpp
index ae5848c..64d8b89 100644
--- a/broadcastradio/1.1/default/Tuner.cpp
+++ b/broadcastradio/1.1/default/Tuner.cpp
@@ -14,15 +14,13 @@
* limitations under the License.
*/
-#define LOG_TAG "Tuner"
-//#define LOG_NDEBUG 0
-
-#include <log/log.h>
+#define LOG_TAG "BroadcastRadioDefault.tuner"
+#define LOG_NDEBUG 0
#include "BroadcastRadio.h"
#include "Tuner.h"
-#include "Utils.h"
-#include <system/RadioMetadataWrapper.h>
+
+#include <log/log.h>
namespace android {
namespace hardware {
@@ -30,199 +28,283 @@
namespace V1_1 {
namespace implementation {
-void Tuner::onCallback(radio_hal_event_t *halEvent)
-{
- BandConfig config;
- ProgramInfo info;
- hidl_vec<MetaData> metadata;
+using namespace std::chrono_literals;
- if (mCallback != 0) {
- switch(halEvent->type) {
- case RADIO_EVENT_CONFIG:
- Utils::convertBandConfigFromHal(&config, &halEvent->config);
- mCallback->configChange(Utils::convertHalResult(halEvent->status), config);
- break;
- case RADIO_EVENT_ANTENNA:
- mCallback->antennaStateChange(halEvent->on);
- break;
- case RADIO_EVENT_TUNED:
- Utils::convertProgramInfoFromHal(&info, &halEvent->info);
- if (mCallback1_1 != nullptr) {
- mCallback1_1->tuneComplete_1_1(Utils::convertHalResult(halEvent->status), info);
- }
- mCallback->tuneComplete(Utils::convertHalResult(halEvent->status), info.base);
- break;
- case RADIO_EVENT_METADATA: {
- uint32_t channel;
- uint32_t sub_channel;
- if (radio_metadata_get_channel(halEvent->metadata, &channel, &sub_channel) == 0) {
- Utils::convertMetaDataFromHal(metadata, halEvent->metadata);
- mCallback->newMetadata(channel, sub_channel, metadata);
- }
- } break;
- case RADIO_EVENT_TA:
- mCallback->trafficAnnouncement(halEvent->on);
- break;
- case RADIO_EVENT_AF_SWITCH:
- Utils::convertProgramInfoFromHal(&info, &halEvent->info);
- if (mCallback1_1 != nullptr) {
- mCallback1_1->afSwitch_1_1(info);
- }
- mCallback->afSwitch(info.base);
- break;
- case RADIO_EVENT_EA:
- mCallback->emergencyAnnouncement(halEvent->on);
- break;
- case RADIO_EVENT_HW_FAILURE:
- default:
- mCallback->hardwareFailure();
- break;
- }
- }
+using V1_0::Band;
+using V1_0::BandConfig;
+using V1_0::Direction;
+
+using std::chrono::milliseconds;
+using std::lock_guard;
+using std::move;
+using std::mutex;
+using std::sort;
+using std::vector;
+
+const struct {
+ milliseconds config = 50ms;
+ milliseconds scan = 200ms;
+ milliseconds step = 100ms;
+ milliseconds tune = 150ms;
+} gDefaultDelay;
+
+Tuner::Tuner(const sp<V1_0::ITunerCallback>& callback)
+ : mCallback(callback),
+ mCallback1_1(ITunerCallback::castFrom(callback).withDefault(nullptr)),
+ mVirtualFm(make_fm_radio()) {}
+
+void Tuner::forceClose() {
+ lock_guard<mutex> lk(mMut);
+ mIsClosed = true;
+ mThread.cancelAll();
}
-//static
-void Tuner::callback(radio_hal_event_t *halEvent, void *cookie)
-{
- wp<Tuner> weak(reinterpret_cast<Tuner*>(cookie));
- sp<Tuner> tuner = weak.promote();
- if (tuner == 0) return;
- tuner->onCallback(halEvent);
+Return<Result> Tuner::setConfiguration(const BandConfig& config) {
+ ALOGV("%s", __func__);
+
+ if (config.lowerLimit >= config.upperLimit) return Result::INVALID_ARGUMENTS;
+
+ auto task = [this, config]() {
+ ALOGI("Setting AM/FM config");
+ lock_guard<mutex> lk(mMut);
+
+ mAmfmConfig = move(config);
+ mAmfmConfig.antennaConnected = true;
+ mIsAmfmConfigSet = true;
+ mCallback->configChange(Result::OK, mAmfmConfig);
+ };
+ mThread.schedule(task, gDefaultDelay.config);
+
+ return Result::OK;
}
-Tuner::Tuner(const sp<V1_0::ITunerCallback>& callback, const wp<BroadcastRadio>& parentDevice)
- : mHalTuner(NULL), mCallback(callback), mCallback1_1(ITunerCallback::castFrom(callback)),
- mParentDevice(parentDevice)
-{
- ALOGV("%s", __FUNCTION__);
-}
+Return<void> Tuner::getConfiguration(getConfiguration_cb _hidl_cb) {
+ ALOGV("%s", __func__);
-
-Tuner::~Tuner()
-{
- ALOGV("%s", __FUNCTION__);
- const sp<BroadcastRadio> parentDevice = mParentDevice.promote();
- if (parentDevice != 0) {
- parentDevice->closeHalTuner(mHalTuner);
+ lock_guard<mutex> lk(mMut);
+ if (mIsAmfmConfigSet) {
+ _hidl_cb(Result::OK, mAmfmConfig);
+ } else {
+ _hidl_cb(Result::NOT_INITIALIZED, {});
}
-}
-
-// Methods from ::android::hardware::broadcastradio::V1_1::ITuner follow.
-Return<Result> Tuner::setConfiguration(const BandConfig& config) {
- ALOGV("%s", __FUNCTION__);
- if (mHalTuner == NULL) {
- return Utils::convertHalResult(-ENODEV);
- }
- radio_hal_band_config_t halConfig;
- Utils::convertBandConfigToHal(&halConfig, &config);
- int rc = mHalTuner->set_configuration(mHalTuner, &halConfig);
- return Utils::convertHalResult(rc);
-}
-
-Return<void> Tuner::getConfiguration(getConfiguration_cb _hidl_cb) {
- int rc;
- radio_hal_band_config_t halConfig;
- BandConfig config;
-
- ALOGV("%s", __FUNCTION__);
- if (mHalTuner == NULL) {
- rc = -ENODEV;
- goto exit;
- }
- rc = mHalTuner->get_configuration(mHalTuner, &halConfig);
- if (rc == 0) {
- Utils::convertBandConfigFromHal(&config, &halConfig);
- }
-
-exit:
- _hidl_cb(Utils::convertHalResult(rc), config);
return Void();
}
-Return<Result> Tuner::scan(Direction direction, bool skipSubChannel) {
- if (mHalTuner == NULL) {
- return Utils::convertHalResult(-ENODEV);
- }
- int rc = mHalTuner->scan(mHalTuner, static_cast<radio_direction_t>(direction), skipSubChannel);
- return Utils::convertHalResult(rc);
+// makes ProgramInfo that points to no channel
+static ProgramInfo makeDummyProgramInfo(uint32_t channel) {
+ ProgramInfo info11 = {};
+ auto& info10 = info11.base;
+
+ info10.channel = channel;
+ info11.flags |= ProgramInfoFlags::MUTED;
+
+ return info11;
}
-Return<Result> Tuner::step(Direction direction, bool skipSubChannel) {
- if (mHalTuner == NULL) {
- return Utils::convertHalResult(-ENODEV);
+void Tuner::tuneInternalLocked() {
+ VirtualRadio* virtualRadio = nullptr;
+ if (mAmfmConfig.type == Band::FM_HD || mAmfmConfig.type == Band::FM) {
+ virtualRadio = &mVirtualFm;
}
- int rc = mHalTuner->step(mHalTuner, static_cast<radio_direction_t>(direction), skipSubChannel);
- return Utils::convertHalResult(rc);
+
+ auto& info11 = mCurrentProgramInfo;
+ auto& info10 = info11.base;
+
+ VirtualProgram virtualProgram;
+ if (virtualRadio != nullptr && virtualRadio->getProgram(mCurrentProgram, virtualProgram)) {
+ // TODO(b/36864090): convert virtualProgram to ProgramInfo instead
+ info10.channel = mCurrentProgram;
+ info10.tuned = true;
+ info10.stereo = true;
+ info10.signalStrength = 100;
+ } else {
+ info11 = makeDummyProgramInfo(mCurrentProgram);
+ }
+ mIsTuneCompleted = true;
+
+ mCallback->tuneComplete(Result::OK, info10);
+ if (mCallback1_1 != nullptr) {
+ mCallback1_1->tuneComplete_1_1(Result::OK, info11);
+ }
+}
+
+Return<Result> Tuner::scan(Direction direction, bool skipSubChannel __unused) {
+ ALOGV("%s", __func__);
+ lock_guard<mutex> lk(mMut);
+ vector<VirtualProgram> list;
+
+ if (mAmfmConfig.type == Band::FM_HD || mAmfmConfig.type == Band::FM) {
+ list = mVirtualFm.getProgramList();
+ }
+
+ if (list.size() == 0) {
+ mIsTuneCompleted = false;
+ auto task = [this, direction]() {
+ ALOGI("Performing failed scan %s", toString(direction).c_str());
+
+ mCallback->tuneComplete(Result::TIMEOUT, {});
+ if (mCallback1_1 != nullptr) {
+ mCallback1_1->tuneComplete_1_1(Result::TIMEOUT, {});
+ }
+ };
+ mThread.schedule(task, gDefaultDelay.scan);
+
+ return Result::OK;
+ }
+
+ // Not optimal (O(sort) instead of O(n)), but not a big deal here;
+ // also, it's likely that list is already sorted (so O(n) anyway).
+ sort(list.begin(), list.end());
+ auto current = mCurrentProgram;
+ auto found = lower_bound(list.begin(), list.end(), VirtualProgram({current}));
+ if (direction == Direction::UP) {
+ if (found < list.end() - 1) {
+ if (found->channel == current) found++;
+ } else {
+ found = list.begin();
+ }
+ } else {
+ if (found > list.begin() && found != list.end()) {
+ found--;
+ } else {
+ found = list.end() - 1;
+ }
+ }
+ auto tuneTo = found->channel;
+
+ mIsTuneCompleted = false;
+ auto task = [this, tuneTo, direction]() {
+ ALOGI("Performing scan %s", toString(direction).c_str());
+
+ lock_guard<mutex> lk(mMut);
+ mCurrentProgram = tuneTo;
+ tuneInternalLocked();
+ };
+ mThread.schedule(task, gDefaultDelay.scan);
+
+ return Result::OK;
+}
+
+Return<Result> Tuner::step(Direction direction, bool skipSubChannel __unused) {
+ ALOGV("%s", __func__);
+
+ lock_guard<mutex> lk(mMut);
+ ALOGW_IF(!mIsAmfmConfigSet, "AM/FM config not set");
+ if (!mIsAmfmConfigSet) return Result::INVALID_STATE;
+ mIsTuneCompleted = false;
+
+ auto task = [this, direction]() {
+ ALOGI("Performing step %s", toString(direction).c_str());
+
+ lock_guard<mutex> lk(mMut);
+
+ if (direction == Direction::UP) {
+ mCurrentProgram += mAmfmConfig.spacings[0];
+ } else {
+ mCurrentProgram -= mAmfmConfig.spacings[0];
+ }
+
+ if (mCurrentProgram > mAmfmConfig.upperLimit) mCurrentProgram = mAmfmConfig.lowerLimit;
+ if (mCurrentProgram < mAmfmConfig.lowerLimit) mCurrentProgram = mAmfmConfig.upperLimit;
+
+ tuneInternalLocked();
+ };
+ mThread.schedule(task, gDefaultDelay.step);
+
+ return Result::OK;
}
Return<Result> Tuner::tune(uint32_t channel, uint32_t subChannel) {
- if (mHalTuner == NULL) {
- return Utils::convertHalResult(-ENODEV);
+ ALOGV("%s(%d, %d)", __func__, channel, subChannel);
+
+ 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;
}
- int rc = mHalTuner->tune(mHalTuner, channel, subChannel);
- return Utils::convertHalResult(rc);
+ mIsTuneCompleted = false;
+
+ auto task = [this, channel]() {
+ lock_guard<mutex> lk(mMut);
+ mCurrentProgram = channel;
+ tuneInternalLocked();
+ };
+ mThread.schedule(task, gDefaultDelay.tune);
+
+ return Result::OK;
}
Return<Result> Tuner::cancel() {
- if (mHalTuner == NULL) {
- return Utils::convertHalResult(-ENODEV);
- }
- int rc = mHalTuner->cancel(mHalTuner);
- return Utils::convertHalResult(rc);
+ ALOGV("%s", __func__);
+ mThread.cancelAll();
+ return Result::OK;
}
Return<void> Tuner::getProgramInformation(getProgramInformation_cb _hidl_cb) {
- ALOGV("%s", __FUNCTION__);
+ 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) {
- int rc;
- radio_program_info_t halInfo;
- RadioMetadataWrapper metadataWrapper(&halInfo.metadata);
- ProgramInfo info;
+ ALOGV("%s", __func__);
- ALOGV("%s", __FUNCTION__);
- if (mHalTuner == NULL) {
- rc = -ENODEV;
- goto exit;
+ lock_guard<mutex> lk(mMut);
+ if (mIsTuneCompleted) {
+ _hidl_cb(Result::OK, mCurrentProgramInfo);
+ } else {
+ _hidl_cb(Result::NOT_INITIALIZED, makeDummyProgramInfo(mCurrentProgram));
}
-
- rc = mHalTuner->get_program_information(mHalTuner, &halInfo);
- if (rc == 0) {
- Utils::convertProgramInfoFromHal(&info, &halInfo);
- }
-
-exit:
- _hidl_cb(Utils::convertHalResult(rc), info);
return Void();
}
Return<ProgramListResult> Tuner::startBackgroundScan() {
+ ALOGV("%s", __func__);
return ProgramListResult::UNAVAILABLE;
}
Return<void> Tuner::getProgramList(const hidl_string& filter __unused, getProgramList_cb _hidl_cb) {
- hidl_vec<ProgramInfo> pList;
- // TODO(b/34054813): do the actual implementation.
- _hidl_cb(ProgramListResult::NOT_STARTED, pList);
+ ALOGV("%s", __func__);
+
+ auto& virtualRadio = mVirtualFm;
+ if (mAmfmConfig.type != Band::FM_HD && mAmfmConfig.type != Band::FM) {
+ _hidl_cb(ProgramListResult::OK, {});
+ return Void();
+ }
+
+ hidl_vec<ProgramInfo> list;
+ auto vList = virtualRadio.getProgramList();
+ list.resize(vList.size());
+ for (size_t i = 0; i < vList.size(); i++) {
+ auto& src = vList[i];
+ auto& dst11 = list[i];
+ auto& dst10 = dst11.base;
+
+ // TODO(b/36864090): convert virtualProgram to ProgramInfo instead
+ dst10.channel = src.channel;
+ dst10.tuned = true;
+ }
+
+ _hidl_cb(ProgramListResult::OK, list);
return Void();
}
Return<void> Tuner::isAnalogForced(isAnalogForced_cb _hidl_cb) {
- // TODO(b/34348946): do the actual implementation.
+ ALOGV("%s", __func__);
+ // TODO(b/36864090): implement
_hidl_cb(Result::INVALID_STATE, false);
return Void();
}
Return<Result> Tuner::setAnalogForced(bool isForced __unused) {
- // TODO(b/34348946): do the actual implementation.
+ ALOGV("%s", __func__);
+ // TODO(b/36864090): implement
return Result::INVALID_STATE;
}
-} // namespace implementation
+} // namespace implementation
} // namespace V1_1
} // namespace broadcastradio
} // namespace hardware
diff --git a/broadcastradio/1.1/default/Tuner.h b/broadcastradio/1.1/default/Tuner.h
index 57eafd3..7719d4d 100644
--- a/broadcastradio/1.1/default/Tuner.h
+++ b/broadcastradio/1.1/default/Tuner.h
@@ -16,6 +16,9 @@
#ifndef ANDROID_HARDWARE_BROADCASTRADIO_V1_1_TUNER_H
#define ANDROID_HARDWARE_BROADCASTRADIO_V1_1_TUNER_H
+#include "VirtualRadio.h"
+
+#include <WorkerThread.h>
#include <android/hardware/broadcastradio/1.1/ITuner.h>
#include <android/hardware/broadcastradio/1.1/ITunerCallback.h>
@@ -25,19 +28,16 @@
namespace V1_1 {
namespace implementation {
-using V1_0::Direction;
-
-struct BroadcastRadio;
-
struct Tuner : public ITuner {
+ Tuner(const sp<V1_0::ITunerCallback>& callback);
- Tuner(const sp<V1_0::ITunerCallback>& callback, const wp<BroadcastRadio>& mParentDevice);
+ void forceClose();
- // Methods from ::android::hardware::broadcastradio::V1_1::ITuner follow.
- Return<Result> setConfiguration(const BandConfig& config) override;
+ // V1_1::ITuner methods
+ Return<Result> setConfiguration(const V1_0::BandConfig& config) override;
Return<void> getConfiguration(getConfiguration_cb _hidl_cb) override;
- Return<Result> scan(Direction direction, bool skipSubChannel) override;
- Return<Result> step(Direction direction, bool skipSubChannel) override;
+ 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> cancel() override;
Return<void> getProgramInformation(getProgramInformation_cb _hidl_cb) override;
@@ -47,21 +47,24 @@
Return<void> isAnalogForced(isAnalogForced_cb _hidl_cb) override;
Return<Result> setAnalogForced(bool isForced) override;
- static void callback(radio_hal_event_t *halEvent, void *cookie);
- void onCallback(radio_hal_event_t *halEvent);
+ private:
+ std::mutex mMut;
+ WorkerThread mThread;
+ bool mIsClosed = false; // TODO(b/36864090): use it
- void setHalTuner(const struct radio_tuner *halTuner) { mHalTuner = halTuner; }
- const struct radio_tuner *getHalTuner() { return mHalTuner; }
-
-private:
- ~Tuner();
-
- const struct radio_tuner *mHalTuner;
const sp<V1_0::ITunerCallback> mCallback;
const sp<V1_1::ITunerCallback> mCallback1_1;
- const wp<BroadcastRadio> mParentDevice;
-};
+ VirtualRadio mVirtualFm;
+
+ bool mIsAmfmConfigSet = false;
+ V1_0::BandConfig mAmfmConfig;
+ bool mIsTuneCompleted = false;
+ uint32_t mCurrentProgram; // TODO(b/32621193): Station Selector
+ ProgramInfo mCurrentProgramInfo = {};
+
+ void tuneInternalLocked();
+};
} // namespace implementation
} // namespace V1_1
diff --git a/broadcastradio/1.1/default/Utils.cpp b/broadcastradio/1.1/default/Utils.cpp
deleted file mode 100644
index e21344e..0000000
--- a/broadcastradio/1.1/default/Utils.cpp
+++ /dev/null
@@ -1,299 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-#define LOG_TAG "BroadcastRadioHalUtils"
-//#define LOG_NDEBUG 0
-
-#include <log/log.h>
-#include <system/radio_metadata.h>
-
-#include "Utils.h"
-
-namespace android {
-namespace hardware {
-namespace broadcastradio {
-namespace V1_1 {
-namespace implementation {
-
-using V1_0::Band;
-using V1_0::Deemphasis;
-using V1_0::Direction;
-using V1_0::MetadataKey;
-using V1_0::MetadataType;
-using V1_0::Rds;
-
-const char *Utils::sClassModuleNames[] = {
- RADIO_HARDWARE_MODULE_ID_FM, /* corresponds to RADIO_CLASS_AM_FM */
- RADIO_HARDWARE_MODULE_ID_SAT, /* corresponds to RADIO_CLASS_SAT */
- RADIO_HARDWARE_MODULE_ID_DT, /* corresponds to RADIO_CLASS_DT */
-};
-
-// make sure HIDL enum values are aligned with legacy values
-static_assert(RADIO_CLASS_AM_FM == static_cast<int>(Class::AM_FM),
- "AM/FM class mismatch with legacy");
-static_assert(RADIO_CLASS_SAT == static_cast<int>(Class::SAT),
- "SAT class mismatch with legacy");
-static_assert(RADIO_CLASS_DT == static_cast<int>(Class::DT),
- "DT class mismatch with legacy");
-
-static_assert(RADIO_BAND_AM == static_cast<int>(Band::AM),
- "AM band mismatch with legacy");
-static_assert(RADIO_BAND_FM == static_cast<int>(Band::FM),
- "FM band mismatch with legacy");
-static_assert(RADIO_BAND_AM_HD == static_cast<int>(Band::AM_HD),
- "AM HD band mismatch with legacy");
-static_assert(RADIO_BAND_FM_HD == static_cast<int>(Band::FM_HD),
- "FM HD band mismatch with legacy");
-
-static_assert(RADIO_RDS_NONE == static_cast<int>(Rds::NONE),
- "RDS NONE mismatch with legacy");
-static_assert(RADIO_RDS_WORLD == static_cast<int>(Rds::WORLD),
- "RDS WORLD mismatch with legacy");
-static_assert(RADIO_RDS_US == static_cast<int>(Rds::US),
- "RDS US mismatch with legacy");
-
-static_assert(RADIO_DEEMPHASIS_50 == static_cast<int>(Deemphasis::D50),
- "De-emphasis 50 mismatch with legacy");
-static_assert(RADIO_DEEMPHASIS_75 == static_cast<int>(Deemphasis::D75),
- "De-emphasis 75 mismatch with legacy");
-
-static_assert(RADIO_DIRECTION_UP == static_cast<int>(Direction::UP),
- "Direction Up mismatch with legacy");
-static_assert(RADIO_DIRECTION_DOWN == static_cast<int>(Direction::DOWN),
- "Direction Up mismatch with legacy");
-
-static_assert(RADIO_METADATA_TYPE_INVALID == static_cast<int>(MetadataType::INVALID),
- "Metadata type INVALID mismatch with legacy");
-static_assert(RADIO_METADATA_TYPE_INT == static_cast<int>(MetadataType::INT),
- "Metadata type INT mismatch with legacy");
-static_assert(RADIO_METADATA_TYPE_TEXT == static_cast<int>(MetadataType::TEXT),
- "Metadata type TEXT mismatch with legacy");
-static_assert(RADIO_METADATA_TYPE_RAW == static_cast<int>(MetadataType::RAW),
- "Metadata type RAW mismatch with legacy");
-static_assert(RADIO_METADATA_TYPE_CLOCK == static_cast<int>(MetadataType::CLOCK),
- "Metadata type CLOCK mismatch with legacy");
-
-static_assert(RADIO_METADATA_KEY_INVALID == static_cast<int>(MetadataKey::INVALID),
- "Metadata key INVALID mismatch with legacy");
-static_assert(RADIO_METADATA_KEY_RDS_PI == static_cast<int>(MetadataKey::RDS_PI),
- "Metadata key RDS_PI mismatch with legacy");
-static_assert(RADIO_METADATA_KEY_RDS_PS == static_cast<int>(MetadataKey::RDS_PS),
- "Metadata key RDS_PS mismatch with legacy");
-static_assert(RADIO_METADATA_KEY_RDS_PTY == static_cast<int>(MetadataKey::RDS_PTY),
- "Metadata key RDS_PTY mismatch with legacy");
-static_assert(RADIO_METADATA_KEY_RBDS_PTY == static_cast<int>(MetadataKey::RBDS_PTY),
- "Metadata key RBDS_PTY mismatch with legacy");
-static_assert(RADIO_METADATA_KEY_RDS_RT == static_cast<int>(MetadataKey::RDS_RT),
- "Metadata key RDS_RT mismatch with legacy");
-static_assert(RADIO_METADATA_KEY_TITLE == static_cast<int>(MetadataKey::TITLE),
- "Metadata key TITLE mismatch with legacy");
-static_assert(RADIO_METADATA_KEY_ARTIST == static_cast<int>(MetadataKey::ARTIST),
- "Metadata key ARTIST mismatch with legacy");
-static_assert(RADIO_METADATA_KEY_ALBUM == static_cast<int>(MetadataKey::ALBUM),
- "Metadata key ALBUM mismatch with legacy");
-static_assert(RADIO_METADATA_KEY_GENRE == static_cast<int>(MetadataKey::GENRE),
- "Metadata key GENRE mismatch with legacy");
-static_assert(RADIO_METADATA_KEY_ICON == static_cast<int>(MetadataKey::ICON),
- "Metadata key ICON mismatch with legacy");
-static_assert(RADIO_METADATA_KEY_ART == static_cast<int>(MetadataKey::ART),
- "Metadata key ART mismatch with legacy");
-static_assert(RADIO_METADATA_KEY_CLOCK == static_cast<int>(MetadataKey::CLOCK),
- "Metadata key CLOCK mismatch with legacy");
-
-
-//static
-const char * Utils::getClassString(Class ClassId)
-{
- int id = static_cast<int>(ClassId);
-
- if ((id < 0) ||
- (id >= NELEM(sClassModuleNames))) {
- ALOGE("invalid class ID %d", id);
- return NULL;
- }
- return sClassModuleNames[id];
-}
-
-//static
-Result Utils::convertHalResult(int rc)
-{
- switch (rc) {
- case 0:
- return Result::OK;
- case -EINVAL:
- return Result::INVALID_ARGUMENTS;
- case -ENOSYS:
- return Result::INVALID_STATE;
- case -ETIMEDOUT:
- return Result::TIMEOUT;
- case -ENODEV:
- default:
- return Result::NOT_INITIALIZED;
- }
-}
-
-//static
-void Utils::convertBandConfigFromHal(
- BandConfig *config,
- const radio_hal_band_config_t *halConfig)
-{
-
- config->type = static_cast<Band>(halConfig->type);
- config->antennaConnected = halConfig->antenna_connected;
- config->lowerLimit = halConfig->lower_limit;
- config->upperLimit = halConfig->upper_limit;
- config->spacings.setToExternal(const_cast<unsigned int *>(&halConfig->spacings[0]),
- halConfig->num_spacings * sizeof(uint32_t));
- // FIXME: transfer buffer ownership. should have a method for that in hidl_vec
- config->spacings.resize(halConfig->num_spacings);
-
- if (config->type == Band::FM) {
- config->ext.fm.deemphasis = static_cast<Deemphasis>(halConfig->fm.deemphasis);
- config->ext.fm.stereo = halConfig->fm.stereo;
- config->ext.fm.rds = static_cast<Rds>(halConfig->fm.rds);
- config->ext.fm.ta = halConfig->fm.ta;
- config->ext.fm.af = halConfig->fm.af;
- config->ext.fm.ea = halConfig->fm.ea;
- } else {
- config->ext.am.stereo = halConfig->am.stereo;
- }
-}
-
-//static
-void Utils::convertPropertiesFromHal(Properties *properties,
- const radio_hal_properties_t *halProperties)
-{
- properties->classId = static_cast<Class>(halProperties->class_id);
- properties->implementor.setToExternal(halProperties->implementor, strlen(halProperties->implementor));
- properties->product.setToExternal(halProperties->product, strlen(halProperties->product));
- properties->version.setToExternal(halProperties->version, strlen(halProperties->version));
- properties->serial.setToExternal(halProperties->serial, strlen(halProperties->serial));
- properties->numTuners = halProperties->num_tuners;
- properties->numAudioSources = halProperties->num_audio_sources;
- properties->supportsCapture = halProperties->supports_capture;
-
- BandConfig *bands =
- new BandConfig[halProperties->num_bands];
- for (size_t i = 0; i < halProperties->num_bands; i++) {
- convertBandConfigFromHal(&bands[i], &halProperties->bands[i]);
- }
- properties->bands.setToExternal(bands, halProperties->num_bands);
- // FIXME: transfer buffer ownership. should have a method for that in hidl_vec
- properties->bands.resize(halProperties->num_bands);
- delete[] bands;
-}
-
-//static
-void Utils::convertBandConfigToHal(radio_hal_band_config_t *halConfig, const BandConfig *config)
-{
- halConfig->type = static_cast<radio_band_t>(config->type);
- halConfig->antenna_connected = config->antennaConnected;
- halConfig->lower_limit = config->lowerLimit;
- halConfig->upper_limit = config->upperLimit;
- halConfig->num_spacings = config->spacings.size();
- if (halConfig->num_spacings > RADIO_NUM_SPACINGS_MAX) {
- halConfig->num_spacings = RADIO_NUM_SPACINGS_MAX;
- }
- memcpy(halConfig->spacings, config->spacings.data(),
- sizeof(uint32_t) * halConfig->num_spacings);
-
- if (config->type == Band::FM) {
- halConfig->fm.deemphasis = static_cast<radio_deemphasis_t>(config->ext.fm.deemphasis);
- halConfig->fm.stereo = config->ext.fm.stereo;
- halConfig->fm.rds = static_cast<radio_rds_t>(config->ext.fm.rds);
- halConfig->fm.ta = config->ext.fm.ta;
- halConfig->fm.af = config->ext.fm.af;
- halConfig->fm.ea = config->ext.fm.ea;
- } else {
- halConfig->am.stereo = config->ext.am.stereo;
- }
-}
-
-
-//static
-void Utils::convertProgramInfoFromHal(ProgramInfo *info, radio_program_info_t *halInfo)
-{
- auto &info_1_1 = *info;
- auto &info_1_0 = info->base;
-
- info_1_0.channel = halInfo->channel;
- info_1_0.subChannel = halInfo->sub_channel;
- info_1_0.tuned = halInfo->tuned;
- info_1_0.stereo = halInfo->stereo;
- info_1_0.digital = halInfo->digital;
- info_1_0.signalStrength = halInfo->signal_strength;
- convertMetaDataFromHal(info_1_0.metadata, halInfo->metadata);
- // TODO(b/34348946): add support for HAL 1.1 fields
- info_1_1.flags = 0;
-}
-
-//static
-int Utils::convertMetaDataFromHal(hidl_vec<MetaData>& metadata, radio_metadata_t *halMetadata)
-{
- if (halMetadata == NULL) {
- ALOGE("Invalid argument: halMetadata is NULL");
- return 0;
- }
-
- int count = radio_metadata_get_count(halMetadata);
- if (count <= 0) {
- return count;
- }
- MetaData *newMetadata = new MetaData[count];
- int outCount = 0;
- for (int i = 0; i < count; i++) {
- radio_metadata_key_t key;
- radio_metadata_type_t type;
- void *value;
- size_t size;
- if (radio_metadata_get_at_index(halMetadata, i , &key, &type, &value, &size) != 0 ||
- size == 0) {
- continue;
- }
- switch (type) {
- case RADIO_METADATA_TYPE_INT: {
- newMetadata[outCount].intValue = *(static_cast<int32_t *>(value));
- } break;
- case RADIO_METADATA_TYPE_TEXT: {
- newMetadata[outCount].stringValue = static_cast<char *>(value);
- } break;
- case RADIO_METADATA_TYPE_RAW: {
- newMetadata[outCount].rawValue.setToExternal(static_cast<uint8_t *>(value), size);
- // FIXME: transfer buffer ownership. should have a method for that in hidl_vec
- newMetadata[outCount].rawValue.resize(size);
- } break;
- case RADIO_METADATA_TYPE_CLOCK: {
- radio_metadata_clock_t *clock = static_cast<radio_metadata_clock_t *>(value);
- newMetadata[outCount].clockValue.utcSecondsSinceEpoch =
- clock->utc_seconds_since_epoch;
- newMetadata[outCount].clockValue.timezoneOffsetInMinutes =
- clock->timezone_offset_in_minutes;
- } break;
- }
- newMetadata[outCount].type = static_cast<MetadataType>(type);
- newMetadata[outCount].key = static_cast<MetadataKey>(key);
- outCount++;
- }
- metadata.setToExternal(newMetadata, outCount);
- // FIXME: transfer buffer ownership. should have a method for that in hidl_vec
- metadata.resize(outCount);
- return outCount;
-}
-
-} // namespace implementation
-} // namespace V1_1
-} // namespace broadcastradio
-} // namespace hardware
-} // namespace android
diff --git a/broadcastradio/1.1/default/Utils.h b/broadcastradio/1.1/default/Utils.h
deleted file mode 100644
index 22902ba..0000000
--- a/broadcastradio/1.1/default/Utils.h
+++ /dev/null
@@ -1,58 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-#ifndef ANDROID_HARDWARE_BROADCASTRADIO_V1_1_UTILS_H
-#define ANDROID_HARDWARE_BROADCASTRADIO_V1_1_UTILS_H
-
-#include <android/hardware/broadcastradio/1.1/types.h>
-#include <hardware/radio.h>
-
-namespace android {
-namespace hardware {
-namespace broadcastradio {
-namespace V1_1 {
-namespace implementation {
-
-using V1_0::Class;
-using V1_0::BandConfig;
-using V1_0::MetaData;
-using V1_0::Properties;
-
-class Utils {
-public:
- static const char * getClassString(Class ClassId);
- static Result convertHalResult(int rc);
- static void convertBandConfigFromHal(BandConfig *config,
- const radio_hal_band_config_t *halConfig);
- static void convertPropertiesFromHal(Properties *properties,
- const radio_hal_properties_t *halProperties);
- static void convertBandConfigToHal(radio_hal_band_config_t *halConfig,
- const BandConfig *config);
- static void convertProgramInfoFromHal(ProgramInfo *info,
- radio_program_info_t *halInfo);
- static int convertMetaDataFromHal(hidl_vec<MetaData>& metadata,
- radio_metadata_t *halMetadata);
-private:
- static const char * sClassModuleNames[];
-
-};
-
-} // namespace implementation
-} // namespace V1_1
-} // namespace broadcastradio
-} // namespace hardware
-} // namespace android
-
-#endif // ANDROID_HARDWARE_BROADCASTRADIO_V1_1_UTILS_H
diff --git a/broadcastradio/1.1/default/VirtualProgram.cpp b/broadcastradio/1.1/default/VirtualProgram.cpp
new file mode 100644
index 0000000..b418bf7
--- /dev/null
+++ b/broadcastradio/1.1/default/VirtualProgram.cpp
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#include "VirtualProgram.h"
+
+namespace android {
+namespace hardware {
+namespace broadcastradio {
+namespace V1_1 {
+namespace implementation {
+
+bool operator<(const VirtualProgram& lhs, const VirtualProgram& rhs) {
+ return lhs.channel < rhs.channel;
+}
+
+} // namespace implementation
+} // namespace V1_1
+} // namespace broadcastradio
+} // namespace hardware
+} // namespace android
diff --git a/broadcastradio/1.1/default/VirtualProgram.h b/broadcastradio/1.1/default/VirtualProgram.h
new file mode 100644
index 0000000..ff44590
--- /dev/null
+++ b/broadcastradio/1.1/default/VirtualProgram.h
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#ifndef ANDROID_HARDWARE_BROADCASTRADIO_V1_1_VIRTUALPROGRAM_H
+#define ANDROID_HARDWARE_BROADCASTRADIO_V1_1_VIRTUALPROGRAM_H
+
+#include <cstdint>
+
+namespace android {
+namespace hardware {
+namespace broadcastradio {
+namespace V1_1 {
+namespace implementation {
+
+struct VirtualProgram {
+ uint32_t channel; // TODO(b/32621193): Station Selector
+
+ friend bool operator<(const VirtualProgram& lhs, const VirtualProgram& rhs);
+};
+
+} // namespace implementation
+} // namespace V1_1
+} // namespace broadcastradio
+} // namespace hardware
+} // namespace android
+
+#endif // ANDROID_HARDWARE_BROADCASTRADIO_V1_1_VIRTUALPROGRAM_H
diff --git a/broadcastradio/1.1/default/VirtualRadio.cpp b/broadcastradio/1.1/default/VirtualRadio.cpp
new file mode 100644
index 0000000..017a01f
--- /dev/null
+++ b/broadcastradio/1.1/default/VirtualRadio.cpp
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#include "VirtualRadio.h"
+
+namespace android {
+namespace hardware {
+namespace broadcastradio {
+namespace V1_1 {
+namespace implementation {
+
+using std::lock_guard;
+using std::move;
+using std::mutex;
+using std::vector;
+
+vector<VirtualProgram> gInitialFmPrograms{
+ {94900}, {96500}, {97300}, {99700}, {101300}, {103700}, {106100},
+};
+
+VirtualRadio::VirtualRadio(VirtualRadio&& o) : mPrograms(move(o.mPrograms)) {}
+
+VirtualRadio::VirtualRadio(vector<VirtualProgram> initialList) : mPrograms(initialList) {}
+
+vector<VirtualProgram> VirtualRadio::getProgramList() {
+ lock_guard<mutex> lk(mMut);
+ return mPrograms;
+}
+
+bool VirtualRadio::getProgram(uint32_t channel, VirtualProgram& programOut) {
+ lock_guard<mutex> lk(mMut);
+ for (auto&& program : mPrograms) {
+ if (program.channel == channel) {
+ programOut = program;
+ return true;
+ }
+ }
+ return false;
+}
+
+VirtualRadio make_fm_radio() {
+ return VirtualRadio(gInitialFmPrograms);
+}
+
+} // namespace implementation
+} // namespace V1_1
+} // namespace broadcastradio
+} // namespace hardware
+} // namespace android
diff --git a/broadcastradio/1.1/default/VirtualRadio.h b/broadcastradio/1.1/default/VirtualRadio.h
new file mode 100644
index 0000000..e1918a0
--- /dev/null
+++ b/broadcastradio/1.1/default/VirtualRadio.h
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#ifndef ANDROID_HARDWARE_BROADCASTRADIO_V1_1_VIRTUALRADIO_H
+#define ANDROID_HARDWARE_BROADCASTRADIO_V1_1_VIRTUALRADIO_H
+
+#include "VirtualProgram.h"
+
+#include <mutex>
+#include <vector>
+
+namespace android {
+namespace hardware {
+namespace broadcastradio {
+namespace V1_1 {
+namespace implementation {
+
+class VirtualRadio {
+ public:
+ VirtualRadio(VirtualRadio&& o);
+ VirtualRadio(std::vector<VirtualProgram> initialList);
+
+ std::vector<VirtualProgram> getProgramList();
+ bool getProgram(uint32_t channel, VirtualProgram& program);
+
+ private:
+ std::mutex mMut;
+ std::vector<VirtualProgram> mPrograms;
+};
+
+VirtualRadio make_fm_radio();
+
+} // namespace implementation
+} // namespace V1_1
+} // namespace broadcastradio
+} // namespace hardware
+} // namespace android
+
+#endif // ANDROID_HARDWARE_BROADCASTRADIO_V1_1_VIRTUALRADIO_H
diff --git a/broadcastradio/1.1/tests/Android.bp b/broadcastradio/1.1/tests/Android.bp
new file mode 100644
index 0000000..a9d1562
--- /dev/null
+++ b/broadcastradio/1.1/tests/Android.bp
@@ -0,0 +1,28 @@
+//
+// Copyright (C) 2017 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+cc_test {
+ name: "android.hardware.broadcastradio@1.1-utils-tests",
+ cflags: [
+ "-Wall",
+ "-Wextra",
+ "-Werror",
+ ],
+ srcs: [
+ "WorkerThread_test.cpp",
+ ],
+ static_libs: ["android.hardware.broadcastradio@1.1-utils-lib"],
+}
diff --git a/broadcastradio/1.1/tests/WorkerThread_test.cpp b/broadcastradio/1.1/tests/WorkerThread_test.cpp
new file mode 100644
index 0000000..a0e0ebb
--- /dev/null
+++ b/broadcastradio/1.1/tests/WorkerThread_test.cpp
@@ -0,0 +1,138 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <WorkerThread.h>
+#include <gtest/gtest.h>
+
+namespace {
+
+using namespace std::chrono_literals;
+
+using android::WorkerThread;
+
+using std::atomic;
+using std::chrono::time_point;
+using std::chrono::steady_clock;
+using std::is_sorted;
+using std::lock_guard;
+using std::mutex;
+using std::this_thread::sleep_for;
+using std::vector;
+
+#define ASSERT_EQ_WITH_TOLERANCE(val1, val2, tolerance) \
+ ASSERT_LE((val1) - (tolerance), (val2)); \
+ ASSERT_GE((val1) + (tolerance), (val2));
+
+TEST(WorkerThreadTest, oneTask) {
+ atomic<bool> executed(false);
+ atomic<time_point<steady_clock>> stop;
+ WorkerThread thread;
+
+ auto start = steady_clock::now();
+ thread.schedule(
+ [&]() {
+ stop = steady_clock::now();
+ executed = true;
+ },
+ 100ms);
+
+ sleep_for(150ms);
+
+ ASSERT_TRUE(executed);
+ auto delta = stop.load() - start;
+ ASSERT_EQ_WITH_TOLERANCE(delta, 100ms, 50ms);
+}
+
+TEST(WorkerThreadTest, cancelSecond) {
+ atomic<bool> executed1(false);
+ atomic<bool> executed2(false);
+ WorkerThread thread;
+
+ thread.schedule([&]() { executed2 = true; }, 100ms);
+ thread.schedule([&]() { executed1 = true; }, 25ms);
+
+ sleep_for(50ms);
+ thread.cancelAll();
+ sleep_for(100ms);
+
+ ASSERT_TRUE(executed1);
+ ASSERT_FALSE(executed2);
+}
+
+TEST(WorkerThreadTest, executeInOrder) {
+ mutex mut;
+ vector<int> order;
+ WorkerThread thread;
+
+ thread.schedule(
+ [&]() {
+ lock_guard<mutex> lk(mut);
+ order.push_back(0);
+ },
+ 50ms);
+
+ thread.schedule(
+ [&]() {
+ lock_guard<mutex> lk(mut);
+ order.push_back(4);
+ },
+ 400ms);
+
+ thread.schedule(
+ [&]() {
+ lock_guard<mutex> lk(mut);
+ order.push_back(1);
+ },
+ 100ms);
+
+ thread.schedule(
+ [&]() {
+ lock_guard<mutex> lk(mut);
+ order.push_back(3);
+ },
+ 300ms);
+
+ thread.schedule(
+ [&]() {
+ lock_guard<mutex> lk(mut);
+ order.push_back(2);
+ },
+ 200ms);
+
+ sleep_for(500ms);
+
+ ASSERT_EQ(5u, order.size());
+ ASSERT_TRUE(is_sorted(order.begin(), order.end()));
+}
+
+TEST(WorkerThreadTest, dontExecuteAfterDestruction) {
+ atomic<bool> executed1(false);
+ atomic<bool> executed2(false);
+ {
+ WorkerThread thread;
+
+ thread.schedule([&]() { executed2 = true; }, 100ms);
+ thread.schedule([&]() { executed1 = true; }, 25ms);
+
+ sleep_for(50ms);
+ }
+ sleep_for(100ms);
+
+ ASSERT_TRUE(executed1);
+ ASSERT_FALSE(executed2);
+}
+
+} // anonymous namespace
diff --git a/broadcastradio/1.1/utils/Android.bp b/broadcastradio/1.1/utils/Android.bp
new file mode 100644
index 0000000..fab6517
--- /dev/null
+++ b/broadcastradio/1.1/utils/Android.bp
@@ -0,0 +1,30 @@
+//
+// Copyright (C) 2017 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+cc_library_static {
+ name: "android.hardware.broadcastradio@1.1-utils-lib",
+ vendor: true,
+ relative_install_path: "hw",
+ cflags: [
+ "-Wall",
+ "-Wextra",
+ "-Werror",
+ ],
+ srcs: [
+ "WorkerThread.cpp",
+ ],
+ export_include_dirs: ["."],
+}
diff --git a/broadcastradio/1.1/utils/WorkerThread.cpp b/broadcastradio/1.1/utils/WorkerThread.cpp
new file mode 100644
index 0000000..a3ceaa1
--- /dev/null
+++ b/broadcastradio/1.1/utils/WorkerThread.cpp
@@ -0,0 +1,89 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "WorkerThread"
+//#define LOG_NDEBUG 0
+
+#include "WorkerThread.h"
+
+#include <log/log.h>
+
+namespace android {
+
+using std::chrono::milliseconds;
+using std::chrono::steady_clock;
+using std::function;
+using std::lock_guard;
+using std::mutex;
+using std::priority_queue;
+using std::this_thread::sleep_for;
+using std::unique_lock;
+
+bool operator<(const WorkerThread::Task& lhs, const WorkerThread::Task& rhs) {
+ return lhs.when > rhs.when;
+}
+
+WorkerThread::WorkerThread() : mIsTerminating(false), mThread(&WorkerThread::threadLoop, this) {}
+
+WorkerThread::~WorkerThread() {
+ ALOGV("%s", __func__);
+ {
+ lock_guard<mutex> lk(mMut);
+ mIsTerminating = true;
+ mCond.notify_one();
+ }
+ mThread.join();
+}
+
+void WorkerThread::schedule(function<void()> task, milliseconds delay) {
+ ALOGV("%s", __func__);
+
+ auto when = steady_clock::now() + delay;
+
+ lock_guard<mutex> lk(mMut);
+ mTasks.push(Task({when, task}));
+ mCond.notify_one();
+}
+
+void WorkerThread::cancelAll() {
+ ALOGV("%s", __func__);
+
+ lock_guard<mutex> lk(mMut);
+ priority_queue<Task>().swap(mTasks); // empty queue
+}
+
+void WorkerThread::threadLoop() {
+ ALOGV("%s", __func__);
+ while (!mIsTerminating) {
+ unique_lock<mutex> lk(mMut);
+ if (mTasks.empty()) {
+ mCond.wait(lk);
+ continue;
+ }
+
+ auto task = mTasks.top();
+ if (task.when > steady_clock::now()) {
+ mCond.wait_until(lk, task.when);
+ continue;
+ }
+
+ mTasks.pop();
+ lk.unlock(); // what() might need to schedule another task
+ task.what();
+ }
+}
+
+} // namespace android
diff --git a/broadcastradio/1.1/utils/WorkerThread.h b/broadcastradio/1.1/utils/WorkerThread.h
new file mode 100644
index 0000000..635876f
--- /dev/null
+++ b/broadcastradio/1.1/utils/WorkerThread.h
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#ifndef ANDROID_HARDWARE_BROADCASTRADIO_V1_1_WORKERTHREAD_H
+#define ANDROID_HARDWARE_BROADCASTRADIO_V1_1_WORKERTHREAD_H
+
+#include <chrono>
+#include <queue>
+#include <thread>
+
+namespace android {
+
+class WorkerThread {
+ public:
+ WorkerThread();
+ virtual ~WorkerThread();
+
+ void schedule(std::function<void()> task, std::chrono::milliseconds delay);
+ void cancelAll();
+
+ private:
+ struct Task {
+ std::chrono::time_point<std::chrono::steady_clock> when;
+ std::function<void()> what;
+ };
+ friend bool operator<(const Task& lhs, const Task& rhs);
+
+ std::atomic<bool> mIsTerminating;
+ std::mutex mMut;
+ std::condition_variable mCond;
+ std::thread mThread;
+ std::priority_queue<Task> mTasks;
+
+ void threadLoop();
+};
+
+} // namespace android
+
+#endif // ANDROID_HARDWARE_BROADCASTRADIO_V1_1_WORKERTHREAD_H
diff --git a/broadcastradio/1.1/vts/functional/VtsHalBroadcastradioV1_1TargetTest.cpp b/broadcastradio/1.1/vts/functional/VtsHalBroadcastradioV1_1TargetTest.cpp
index aa5ab54..904d896 100644
--- a/broadcastradio/1.1/vts/functional/VtsHalBroadcastradioV1_1TargetTest.cpp
+++ b/broadcastradio/1.1/vts/functional/VtsHalBroadcastradioV1_1TargetTest.cpp
@@ -56,14 +56,15 @@
protected:
virtual void SetUp() override {
auto factory = ::testing::VtsHalHidlTargetTestBase::getService<IBroadcastRadioFactory>();
- if (factory != 0) {
- factory->connectModule(Class::AM_FM,
- [&](Result retval, const ::android::sp<IBroadcastRadio>& result) {
- if (retval == Result::OK) {
- mRadio = IBroadcastRadio::castFrom(result);
- }
- });
- }
+ ASSERT_NE(nullptr, factory.get());
+ Result halResult;
+ factory->connectModule(Class::AM_FM, [&](Result retval, const sp<IBroadcastRadio>& result) {
+ halResult = retval;
+ if (retval == Result::OK) {
+ mRadio = IBroadcastRadio::castFrom(result);
+ }
+ });
+ ASSERT_EQ(Result::OK, halResult);
mTunerCallback = new MyCallback(this);
ASSERT_NE(nullptr, mRadio.get());
ASSERT_NE(nullptr, mTunerCallback.get());
@@ -277,6 +278,7 @@
});
EXPECT_TRUE(hidlReturn.isOk());
EXPECT_EQ(Result::OK, halResult);
+ EXPECT_NE(nullptr, mTuner.get());
EXPECT_TRUE(waitForCallback(kConfigCallbacktimeoutNs));
}
EXPECT_NE(nullptr, mTuner.get());
diff --git a/broadcastradio/Android.bp b/broadcastradio/Android.bp
index 7a315fa..a5ad5e7 100644
--- a/broadcastradio/Android.bp
+++ b/broadcastradio/Android.bp
@@ -5,5 +5,7 @@
"1.0/vts/functional",
"1.1",
"1.1/default",
+ "1.1/tests",
+ "1.1/utils",
"1.1/vts/functional",
]