Branch out Broadcast Radio 1.2 HAL.
Bug: 62945293
Change-Id: Ic84f9fe6c17040053257085801208ed527fd07ab
Fixes: 64115813
Test: instrumentation, VTS
diff --git a/broadcastradio/1.2/default/Android.bp b/broadcastradio/1.2/default/Android.bp
new file mode 100644
index 0000000..e42cb1e
--- /dev/null
+++ b/broadcastradio/1.2/default/Android.bp
@@ -0,0 +1,48 @@
+//
+// 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_binary {
+ name: "android.hardware.broadcastradio@1.2-service",
+ init_rc: ["android.hardware.broadcastradio@1.2-service.rc"],
+ vendor: true,
+ relative_install_path: "hw",
+ cflags: [
+ "-Wall",
+ "-Wextra",
+ "-Werror",
+ ],
+ srcs: [
+ "BroadcastRadio.cpp",
+ "BroadcastRadioFactory.cpp",
+ "Tuner.cpp",
+ "VirtualProgram.cpp",
+ "VirtualRadio.cpp",
+ "service.cpp"
+ ],
+ static_libs: [
+ "android.hardware.broadcastradio@common-utils-lib",
+ ],
+ shared_libs: [
+ "android.hardware.broadcastradio@1.0",
+ "android.hardware.broadcastradio@1.1",
+ "android.hardware.broadcastradio@1.2",
+ "libbase",
+ "libhidlbase",
+ "libhidltransport",
+ "liblog",
+ "libutils",
+ ],
+}
diff --git a/broadcastradio/1.2/default/BroadcastRadio.cpp b/broadcastradio/1.2/default/BroadcastRadio.cpp
new file mode 100644
index 0000000..5164e47
--- /dev/null
+++ b/broadcastradio/1.2/default/BroadcastRadio.cpp
@@ -0,0 +1,196 @@
+/*
+ * 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 "BroadcastRadioDefault.module"
+#define LOG_NDEBUG 0
+
+#include "BroadcastRadio.h"
+
+#include <log/log.h>
+
+#include "resources.h"
+
+namespace android {
+namespace hardware {
+namespace broadcastradio {
+namespace V1_2 {
+namespace implementation {
+
+using V1_0::Band;
+using V1_0::BandConfig;
+using V1_0::Class;
+using V1_0::Deemphasis;
+using V1_0::Rds;
+using V1_1::IdentifierType;
+using V1_1::ProgramSelector;
+using V1_1::ProgramType;
+using V1_1::Properties;
+using V1_1::VendorKeyValue;
+
+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,
+ 153, // lowerLimit
+ 26100, // upperLimit
+ {5, 9, 10}, // spacings
+ }),
+ AmFmBandConfig({
+ Band::FM,
+ 65800, // lowerLimit
+ 108000, // upperLimit
+ {10, 100, 200}, // spacings
+ }),
+ AmFmBandConfig({
+ Band::AM_HD,
+ 153, // lowerLimit
+ 26100, // upperLimit
+ {5, 9, 10}, // spacings
+ }),
+ AmFmBandConfig({
+ Band::FM_HD,
+ 87700, // lowerLimit
+ 107900, // upperLimit
+ {200}, // spacings
+ }),
+ },
+ })},
+
+ {Class::SAT, ModuleConfig({
+ "Satellite radio mock",
+ {}, // amFmBands
+ })},
+};
+// clang-format on
+
+BroadcastRadio::BroadcastRadio(Class classId)
+ : mClassId(classId), mConfig(gModuleConfigs.at(classId)) {}
+
+bool BroadcastRadio::isSupported(Class classId) {
+ return gModuleConfigs.find(classId) != gModuleConfigs.end();
+}
+
+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) {
+ ALOGV("%s", __func__);
+ Properties prop11 = {};
+ auto& prop10 = prop11.base;
+
+ prop10.classId = mClassId;
+ prop10.implementor = "Google";
+ prop10.product = mConfig.productName;
+ prop10.numTuners = 1;
+ prop10.numAudioSources = 1;
+ prop10.supportsCapture = false;
+ prop11.supportsBackgroundScanning = false;
+ prop11.supportedProgramTypes = hidl_vec<uint32_t>({
+ static_cast<uint32_t>(ProgramType::AM), static_cast<uint32_t>(ProgramType::FM),
+ static_cast<uint32_t>(ProgramType::AM_HD), static_cast<uint32_t>(ProgramType::FM_HD),
+ });
+ prop11.supportedIdentifierTypes = hidl_vec<uint32_t>({
+ static_cast<uint32_t>(IdentifierType::AMFM_FREQUENCY),
+ static_cast<uint32_t>(IdentifierType::RDS_PI),
+ static_cast<uint32_t>(IdentifierType::HD_STATION_ID_EXT),
+ static_cast<uint32_t>(IdentifierType::HD_SUBCHANNEL),
+ });
+ prop11.vendorInfo = hidl_vec<VendorKeyValue>({
+ {"com.google.dummy", "dummy"},
+ });
+
+ 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];
+
+ dst.type = src.type;
+ dst.antennaConnected = true;
+ dst.lowerLimit = src.lowerLimit;
+ dst.upperLimit = src.upperLimit;
+ dst.spacings = src.spacings;
+
+ if (utils::isAm(src.type)) {
+ dst.ext.am.stereo = true;
+ } else if (utils::isFm(src.type)) {
+ dst.ext.fm.deemphasis = static_cast<Deemphasis>(Deemphasis::D50 | Deemphasis::D75);
+ dst.ext.fm.stereo = true;
+ dst.ext.fm.rds = static_cast<Rds>(Rds::WORLD | Rds::US);
+ dst.ext.fm.ta = true;
+ dst.ext.fm.af = true;
+ dst.ext.fm.ea = true;
+ }
+ }
+
+ _hidl_cb(prop11);
+ return Void();
+}
+
+Return<void> BroadcastRadio::openTuner(const BandConfig& config, bool audio __unused,
+ const sp<V1_0::ITunerCallback>& callback,
+ openTuner_cb _hidl_cb) {
+ ALOGV("%s(%s)", __func__, toString(config.type).c_str());
+ 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(mClassId, 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();
+}
+
+Return<void> BroadcastRadio::getImage(int32_t id, getImage_cb _hidl_cb) {
+ ALOGV("%s(%x)", __func__, id);
+
+ if (id == resources::demoPngId) {
+ _hidl_cb(std::vector<uint8_t>(resources::demoPng, std::end(resources::demoPng)));
+ return {};
+ }
+
+ ALOGI("Image %x doesn't exists", id);
+ _hidl_cb({});
+ return Void();
+}
+
+} // namespace implementation
+} // namespace V1_2
+} // namespace broadcastradio
+} // namespace hardware
+} // namespace android
diff --git a/broadcastradio/1.2/default/BroadcastRadio.h b/broadcastradio/1.2/default/BroadcastRadio.h
new file mode 100644
index 0000000..94d62b9
--- /dev/null
+++ b/broadcastradio/1.2/default/BroadcastRadio.h
@@ -0,0 +1,81 @@
+/*
+ * 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_2_BROADCASTRADIO_H
+#define ANDROID_HARDWARE_BROADCASTRADIO_V1_2_BROADCASTRADIO_H
+
+#include "Tuner.h"
+
+#include <android/hardware/broadcastradio/1.1/IBroadcastRadio.h>
+#include <android/hardware/broadcastradio/1.2/types.h>
+
+namespace android {
+namespace hardware {
+namespace broadcastradio {
+namespace V1_2 {
+namespace implementation {
+
+struct AmFmBandConfig {
+ V1_0::Band type;
+ uint32_t lowerLimit; // kHz
+ uint32_t upperLimit; // kHz
+ std::vector<uint32_t> spacings; // 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);
+
+ /**
+ * Checks, if a given radio type is supported.
+ *
+ * @param classId type of a radio.
+ */
+ static bool isSupported(V1_0::Class classId);
+
+ // 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 V1_0::BandConfig& config, bool audio,
+ const sp<V1_0::ITunerCallback>& callback,
+ openTuner_cb _hidl_cb) override;
+ Return<void> getImage(int32_t id, getImage_cb _hidl_cb);
+
+ private:
+ std::mutex mMut;
+ V1_0::Class mClassId;
+ ModuleConfig mConfig;
+ wp<Tuner> mTuner;
+};
+
+} // namespace implementation
+} // namespace V1_2
+} // namespace broadcastradio
+} // namespace hardware
+} // namespace android
+
+#endif // ANDROID_HARDWARE_BROADCASTRADIO_V1_2_BROADCASTRADIO_H
diff --git a/broadcastradio/1.2/default/BroadcastRadioFactory.cpp b/broadcastradio/1.2/default/BroadcastRadioFactory.cpp
new file mode 100644
index 0000000..8f17aff
--- /dev/null
+++ b/broadcastradio/1.2/default/BroadcastRadioFactory.cpp
@@ -0,0 +1,63 @@
+/*
+ * 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 "BroadcastRadioDefault.factory"
+#define LOG_NDEBUG 0
+
+#include "BroadcastRadioFactory.h"
+
+#include "BroadcastRadio.h"
+
+#include <log/log.h>
+
+namespace android {
+namespace hardware {
+namespace broadcastradio {
+namespace V1_2 {
+namespace implementation {
+
+using V1_0::Class;
+
+using std::vector;
+
+static const vector<Class> gAllClasses = {
+ Class::AM_FM, Class::SAT, Class::DT,
+};
+
+BroadcastRadioFactory::BroadcastRadioFactory() {
+ for (auto&& classId : gAllClasses) {
+ if (!BroadcastRadio::isSupported(classId)) continue;
+ mRadioModules[classId] = new BroadcastRadio(classId);
+ }
+}
+
+Return<void> BroadcastRadioFactory::connectModule(Class classId, connectModule_cb _hidl_cb) {
+ ALOGV("%s(%s)", __func__, toString(classId).c_str());
+
+ 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
+} // namespace V1_2
+} // namespace broadcastradio
+} // namespace hardware
+} // namespace android
diff --git a/broadcastradio/1.2/default/BroadcastRadioFactory.h b/broadcastradio/1.2/default/BroadcastRadioFactory.h
new file mode 100644
index 0000000..c365ae0
--- /dev/null
+++ b/broadcastradio/1.2/default/BroadcastRadioFactory.h
@@ -0,0 +1,45 @@
+/*
+ * 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_2_BROADCASTRADIOFACTORY_H
+#define ANDROID_HARDWARE_BROADCASTRADIO_V1_2_BROADCASTRADIOFACTORY_H
+
+#include <android/hardware/broadcastradio/1.1/IBroadcastRadio.h>
+#include <android/hardware/broadcastradio/1.2/IBroadcastRadioFactory.h>
+#include <android/hardware/broadcastradio/1.2/types.h>
+
+namespace android {
+namespace hardware {
+namespace broadcastradio {
+namespace V1_2 {
+namespace implementation {
+
+struct BroadcastRadioFactory : public IBroadcastRadioFactory {
+ BroadcastRadioFactory();
+
+ // V1_0::IBroadcastRadioFactory methods
+ Return<void> connectModule(V1_0::Class classId, connectModule_cb _hidl_cb) override;
+
+ private:
+ std::map<V1_0::Class, sp<V1_1::IBroadcastRadio>> mRadioModules;
+};
+
+} // namespace implementation
+} // namespace V1_2
+} // namespace broadcastradio
+} // namespace hardware
+} // namespace android
+
+#endif // ANDROID_HARDWARE_BROADCASTRADIO_V1_2_BROADCASTRADIOFACTORY_H
diff --git a/broadcastradio/1.2/default/OWNERS b/broadcastradio/1.2/default/OWNERS
new file mode 100644
index 0000000..136b607
--- /dev/null
+++ b/broadcastradio/1.2/default/OWNERS
@@ -0,0 +1,3 @@
+# Automotive team
+egranata@google.com
+twasilczyk@google.com
diff --git a/broadcastradio/1.2/default/Tuner.cpp b/broadcastradio/1.2/default/Tuner.cpp
new file mode 100644
index 0000000..f5fb643
--- /dev/null
+++ b/broadcastradio/1.2/default/Tuner.cpp
@@ -0,0 +1,389 @@
+/*
+ * 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 "BroadcastRadioDefault.tuner"
+#define LOG_NDEBUG 0
+
+#include "BroadcastRadio.h"
+#include "Tuner.h"
+
+#include <broadcastradio-utils/Utils.h>
+#include <log/log.h>
+
+namespace android {
+namespace hardware {
+namespace broadcastradio {
+namespace V1_2 {
+namespace implementation {
+
+using namespace std::chrono_literals;
+
+using V1_0::Band;
+using V1_0::BandConfig;
+using V1_0::Class;
+using V1_0::Direction;
+using V1_1::IdentifierType;
+using V1_1::ProgramInfo;
+using V1_1::ProgramInfoFlags;
+using V1_1::ProgramListResult;
+using V1_1::ProgramSelector;
+using V1_1::ProgramType;
+using V1_1::VendorKeyValue;
+using utils::HalRevision;
+
+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(V1_0::Class classId, const sp<V1_0::ITunerCallback>& callback)
+ : mClassId(classId),
+ mCallback(callback),
+ mCallback1_1(V1_1::ITunerCallback::castFrom(callback).withDefault(nullptr)),
+ mCallback1_2(V1_2::ITunerCallback::castFrom(callback).withDefault(nullptr)),
+ mVirtualRadio(getRadio(classId)),
+ mIsAnalogForced(false) {}
+
+void Tuner::forceClose() {
+ lock_guard<mutex> lk(mMut);
+ mIsClosed = true;
+ mThread.cancelAll();
+}
+
+Return<Result> Tuner::setConfiguration(const BandConfig& config) {
+ ALOGV("%s", __func__);
+ lock_guard<mutex> lk(mMut);
+ if (mIsClosed) return Result::NOT_INITIALIZED;
+ if (mClassId != Class::AM_FM) {
+ ALOGE("Can't set AM/FM configuration on SAT/DT radio tuner");
+ return Result::INVALID_STATE;
+ }
+
+ 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;
+ mCurrentProgram = utils::make_selector(mAmfmConfig.type, mAmfmConfig.lowerLimit);
+
+ if (utils::isFm(mAmfmConfig.type)) {
+ mVirtualRadio = std::ref(getFmRadio());
+ } else {
+ mVirtualRadio = std::ref(getAmRadio());
+ }
+
+ mIsAmfmConfigSet = true;
+ mCallback->configChange(Result::OK, mAmfmConfig);
+ };
+ mThread.schedule(task, gDefaultDelay.config);
+
+ return Result::OK;
+}
+
+Return<void> Tuner::getConfiguration(getConfiguration_cb _hidl_cb) {
+ ALOGV("%s", __func__);
+ lock_guard<mutex> lk(mMut);
+
+ if (!mIsClosed && mIsAmfmConfigSet) {
+ _hidl_cb(Result::OK, mAmfmConfig);
+ } else {
+ _hidl_cb(Result::NOT_INITIALIZED, {});
+ }
+ return {};
+}
+
+// makes ProgramInfo that points to no program
+static ProgramInfo makeDummyProgramInfo(const ProgramSelector& selector) {
+ ProgramInfo info11 = {};
+ auto& info10 = info11.base;
+
+ utils::getLegacyChannel(selector, &info10.channel, &info10.subChannel);
+ info11.selector = selector;
+ info11.flags |= ProgramInfoFlags::MUTED;
+
+ return info11;
+}
+
+HalRevision Tuner::getHalRev() const {
+ if (mCallback1_2 != nullptr) {
+ return HalRevision::V1_2;
+ } else if (mCallback1_1 != nullptr) {
+ return HalRevision::V1_1;
+ } else {
+ return HalRevision::V1_0;
+ }
+}
+
+void Tuner::tuneInternalLocked(const ProgramSelector& sel) {
+ VirtualProgram virtualProgram;
+ if (mVirtualRadio.get().getProgram(sel, virtualProgram)) {
+ mCurrentProgram = virtualProgram.selector;
+ mCurrentProgramInfo = virtualProgram.getProgramInfo(getHalRev());
+ } else {
+ mCurrentProgram = sel;
+ mCurrentProgramInfo = makeDummyProgramInfo(sel);
+ }
+ mIsTuneCompleted = true;
+
+ if (mCallback1_1 == nullptr) {
+ mCallback->tuneComplete(Result::OK, mCurrentProgramInfo.base);
+ } else {
+ mCallback1_1->tuneComplete_1_1(Result::OK, mCurrentProgramInfo.selector);
+ mCallback1_1->currentProgramInfoChanged(mCurrentProgramInfo);
+ }
+}
+
+Return<Result> Tuner::scan(Direction direction, bool skipSubChannel __unused) {
+ ALOGV("%s", __func__);
+ lock_guard<mutex> lk(mMut);
+ if (mIsClosed) return Result::NOT_INITIALIZED;
+
+ auto list = mVirtualRadio.get().getProgramList();
+
+ if (list.empty()) {
+ mIsTuneCompleted = false;
+ auto task = [this, direction]() {
+ ALOGI("Performing failed scan %s", toString(direction).c_str());
+
+ if (mCallback1_1 == nullptr) {
+ mCallback->tuneComplete(Result::TIMEOUT, {});
+ } else {
+ 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 (utils::tunesTo(current, found->selector)) found++;
+ } else {
+ found = list.begin();
+ }
+ } else {
+ if (found > list.begin() && found != list.end()) {
+ found--;
+ } else {
+ found = list.end() - 1;
+ }
+ }
+ auto tuneTo = found->selector;
+
+ mIsTuneCompleted = false;
+ auto task = [this, tuneTo, direction]() {
+ ALOGI("Performing scan %s", toString(direction).c_str());
+
+ lock_guard<mutex> lk(mMut);
+ tuneInternalLocked(tuneTo);
+ };
+ mThread.schedule(task, gDefaultDelay.scan);
+
+ return Result::OK;
+}
+
+Return<Result> Tuner::step(Direction direction, bool skipSubChannel) {
+ ALOGV("%s", __func__);
+ lock_guard<mutex> lk(mMut);
+ if (mIsClosed) return Result::NOT_INITIALIZED;
+
+ ALOGW_IF(!skipSubChannel, "can't step to next frequency without ignoring subChannel");
+
+ if (!utils::isAmFm(utils::getType(mCurrentProgram))) {
+ ALOGE("Can't step in anything else than AM/FM");
+ return Result::NOT_INITIALIZED;
+ }
+
+ if (!mIsAmfmConfigSet) {
+ ALOGW("AM/FM config not set");
+ return Result::INVALID_STATE;
+ }
+ mIsTuneCompleted = false;
+
+ auto task = [this, direction]() {
+ ALOGI("Performing step %s", toString(direction).c_str());
+
+ lock_guard<mutex> lk(mMut);
+
+ auto current = utils::getId(mCurrentProgram, IdentifierType::AMFM_FREQUENCY, 0);
+
+ if (direction == Direction::UP) {
+ current += mAmfmConfig.spacings[0];
+ } else {
+ current -= mAmfmConfig.spacings[0];
+ }
+
+ if (current > mAmfmConfig.upperLimit) current = mAmfmConfig.lowerLimit;
+ if (current < mAmfmConfig.lowerLimit) current = mAmfmConfig.upperLimit;
+
+ 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) {
+ ALOGV("%s(%d, %d)", __func__, channel, subChannel);
+ Band band;
+ {
+ lock_guard<mutex> lk(mMut);
+ band = mAmfmConfig.type;
+ }
+ return tuneByProgramSelector(utils::make_selector(band, channel, subChannel));
+}
+
+Return<Result> Tuner::tuneByProgramSelector(const ProgramSelector& sel) {
+ ALOGV("%s(%s)", __func__, toString(sel).c_str());
+ lock_guard<mutex> lk(mMut);
+ if (mIsClosed) return Result::NOT_INITIALIZED;
+
+ // checking if ProgramSelector is valid
+ auto programType = utils::getType(sel);
+ if (utils::isAmFm(programType)) {
+ if (!mIsAmfmConfigSet) {
+ ALOGW("AM/FM config not set");
+ return Result::INVALID_STATE;
+ }
+
+ auto freq = utils::getId(sel, IdentifierType::AMFM_FREQUENCY);
+ if (freq < mAmfmConfig.lowerLimit || freq > mAmfmConfig.upperLimit) {
+ return Result::INVALID_ARGUMENTS;
+ }
+ } else if (programType == ProgramType::DAB) {
+ if (!utils::hasId(sel, IdentifierType::DAB_SIDECC)) return Result::INVALID_ARGUMENTS;
+ } else if (programType == ProgramType::DRMO) {
+ if (!utils::hasId(sel, IdentifierType::DRMO_SERVICE_ID)) return Result::INVALID_ARGUMENTS;
+ } else if (programType == ProgramType::SXM) {
+ if (!utils::hasId(sel, IdentifierType::SXM_SERVICE_ID)) return Result::INVALID_ARGUMENTS;
+ } else {
+ return Result::INVALID_ARGUMENTS;
+ }
+
+ mIsTuneCompleted = false;
+ auto task = [this, sel]() {
+ lock_guard<mutex> lk(mMut);
+ tuneInternalLocked(sel);
+ };
+ mThread.schedule(task, gDefaultDelay.tune);
+
+ return Result::OK;
+}
+
+Return<Result> Tuner::cancel() {
+ ALOGV("%s", __func__);
+ lock_guard<mutex> lk(mMut);
+ if (mIsClosed) return Result::NOT_INITIALIZED;
+
+ mThread.cancelAll();
+ return Result::OK;
+}
+
+Return<Result> Tuner::cancelAnnouncement() {
+ ALOGV("%s", __func__);
+ lock_guard<mutex> lk(mMut);
+ if (mIsClosed) return Result::NOT_INITIALIZED;
+
+ return Result::OK;
+}
+
+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) {
+ ALOGV("%s", __func__);
+ lock_guard<mutex> lk(mMut);
+
+ if (mIsClosed) {
+ _hidl_cb(Result::NOT_INITIALIZED, {});
+ } else if (mIsTuneCompleted) {
+ _hidl_cb(Result::OK, mCurrentProgramInfo);
+ } else {
+ _hidl_cb(Result::NOT_INITIALIZED, makeDummyProgramInfo(mCurrentProgram));
+ }
+ return {};
+}
+
+Return<ProgramListResult> Tuner::startBackgroundScan() {
+ ALOGV("%s", __func__);
+ lock_guard<mutex> lk(mMut);
+ if (mIsClosed) return ProgramListResult::NOT_INITIALIZED;
+
+ return ProgramListResult::UNAVAILABLE;
+}
+
+Return<void> Tuner::getProgramList(const hidl_vec<VendorKeyValue>& vendorFilter,
+ getProgramList_cb _hidl_cb) {
+ ALOGV("%s(%s)", __func__, toString(vendorFilter).substr(0, 100).c_str());
+ lock_guard<mutex> lk(mMut);
+ if (mIsClosed) {
+ _hidl_cb(ProgramListResult::NOT_INITIALIZED, {});
+ return {};
+ }
+
+ auto list = mVirtualRadio.get().getProgramList();
+ ALOGD("returning a list of %zu programs", list.size());
+ _hidl_cb(ProgramListResult::OK, getProgramInfoVector(list, getHalRev()));
+ return {};
+}
+
+Return<Result> Tuner::setAnalogForced(bool isForced) {
+ ALOGV("%s", __func__);
+ lock_guard<mutex> lk(mMut);
+ if (mIsClosed) return Result::NOT_INITIALIZED;
+
+ mIsAnalogForced = isForced;
+ return Result::OK;
+}
+
+Return<void> Tuner::isAnalogForced(isAnalogForced_cb _hidl_cb) {
+ ALOGV("%s", __func__);
+ lock_guard<mutex> lk(mMut);
+
+ if (mIsClosed) {
+ _hidl_cb(Result::NOT_INITIALIZED, false);
+ } else {
+ _hidl_cb(Result::OK, mIsAnalogForced);
+ }
+ return {};
+}
+
+} // namespace implementation
+} // namespace V1_2
+} // namespace broadcastradio
+} // namespace hardware
+} // namespace android
diff --git a/broadcastradio/1.2/default/Tuner.h b/broadcastradio/1.2/default/Tuner.h
new file mode 100644
index 0000000..3cf077b
--- /dev/null
+++ b/broadcastradio/1.2/default/Tuner.h
@@ -0,0 +1,81 @@
+/*
+ * 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_2_TUNER_H
+#define ANDROID_HARDWARE_BROADCASTRADIO_V1_2_TUNER_H
+
+#include "VirtualRadio.h"
+
+#include <android/hardware/broadcastradio/1.2/ITuner.h>
+#include <android/hardware/broadcastradio/1.2/ITunerCallback.h>
+#include <broadcastradio-utils/WorkerThread.h>
+
+namespace android {
+namespace hardware {
+namespace broadcastradio {
+namespace V1_2 {
+namespace implementation {
+
+struct Tuner : public ITuner {
+ Tuner(V1_0::Class classId, const sp<V1_0::ITunerCallback>& callback);
+
+ void forceClose();
+
+ // V1_2::ITuner methods
+ virtual Return<Result> setConfiguration(const V1_0::BandConfig& config) override;
+ virtual Return<void> getConfiguration(getConfiguration_cb _hidl_cb) override;
+ virtual Return<Result> scan(V1_0::Direction direction, bool skipSubChannel) override;
+ virtual Return<Result> step(V1_0::Direction direction, bool skipSubChannel) override;
+ virtual Return<Result> tune(uint32_t channel, uint32_t subChannel) override;
+ virtual Return<Result> tuneByProgramSelector(const V1_1::ProgramSelector& program) override;
+ virtual Return<Result> cancel() override;
+ virtual Return<Result> cancelAnnouncement() override;
+ virtual Return<void> getProgramInformation(getProgramInformation_cb _hidl_cb) override;
+ virtual Return<void> getProgramInformation_1_1(getProgramInformation_1_1_cb _hidl_cb) override;
+ virtual Return<V1_1::ProgramListResult> startBackgroundScan() override;
+ virtual Return<void> getProgramList(const hidl_vec<V1_1::VendorKeyValue>& filter,
+ getProgramList_cb _hidl_cb) override;
+ virtual Return<Result> setAnalogForced(bool isForced) override;
+ virtual Return<void> isAnalogForced(isAnalogForced_cb _hidl_cb) override;
+
+ private:
+ std::mutex mMut;
+ WorkerThread mThread;
+ bool mIsClosed = false;
+
+ V1_0::Class mClassId;
+ const sp<V1_0::ITunerCallback> mCallback;
+ const sp<V1_1::ITunerCallback> mCallback1_1;
+ const sp<V1_2::ITunerCallback> mCallback1_2;
+
+ std::reference_wrapper<VirtualRadio> mVirtualRadio;
+ bool mIsAmfmConfigSet = false;
+ V1_0::BandConfig mAmfmConfig;
+ bool mIsTuneCompleted = false;
+ V1_1::ProgramSelector mCurrentProgram = {};
+ V1_1::ProgramInfo mCurrentProgramInfo = {};
+ std::atomic<bool> mIsAnalogForced;
+
+ utils::HalRevision getHalRev() const;
+ void tuneInternalLocked(const V1_1::ProgramSelector& sel);
+};
+
+} // namespace implementation
+} // namespace V1_2
+} // namespace broadcastradio
+} // namespace hardware
+} // namespace android
+
+#endif // ANDROID_HARDWARE_BROADCASTRADIO_V1_2_TUNER_H
diff --git a/broadcastradio/1.2/default/VirtualProgram.cpp b/broadcastradio/1.2/default/VirtualProgram.cpp
new file mode 100644
index 0000000..95879e3
--- /dev/null
+++ b/broadcastradio/1.2/default/VirtualProgram.cpp
@@ -0,0 +1,109 @@
+/*
+ * 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"
+
+#include <broadcastradio-utils/Utils.h>
+
+#include "resources.h"
+
+namespace android {
+namespace hardware {
+namespace broadcastradio {
+namespace V1_2 {
+namespace implementation {
+
+using std::vector;
+
+using V1_0::MetaData;
+using V1_0::MetadataKey;
+using V1_0::MetadataType;
+using V1_1::IdentifierType;
+using V1_1::ProgramInfo;
+using V1_1::VendorKeyValue;
+using utils::HalRevision;
+
+static MetaData createDemoBitmap(MetadataKey key, HalRevision halRev) {
+ MetaData bmp = {MetadataType::INT, key, resources::demoPngId, {}, {}, {}};
+ if (halRev < HalRevision::V1_1) {
+ bmp.type = MetadataType::RAW;
+ bmp.intValue = 0;
+ bmp.rawValue = hidl_vec<uint8_t>(resources::demoPng, std::end(resources::demoPng));
+ }
+ return bmp;
+}
+
+ProgramInfo VirtualProgram::getProgramInfo(HalRevision halRev) const {
+ ProgramInfo info11 = {};
+ auto& info10 = info11.base;
+
+ utils::getLegacyChannel(selector, &info10.channel, &info10.subChannel);
+ info11.selector = selector;
+ info10.tuned = true;
+ info10.stereo = true;
+ info10.digital = utils::isDigital(selector);
+ info10.signalStrength = info10.digital ? 100 : 80;
+
+ info10.metadata = hidl_vec<MetaData>({
+ {MetadataType::TEXT, MetadataKey::RDS_PS, {}, {}, programName, {}},
+ {MetadataType::TEXT, MetadataKey::TITLE, {}, {}, songTitle, {}},
+ {MetadataType::TEXT, MetadataKey::ARTIST, {}, {}, songArtist, {}},
+ createDemoBitmap(MetadataKey::ICON, halRev),
+ createDemoBitmap(MetadataKey::ART, halRev),
+ });
+
+ info11.vendorInfo = hidl_vec<VendorKeyValue>({
+ {"com.google.dummy", "dummy"},
+ {"com.google.dummy.VirtualProgram", std::to_string(reinterpret_cast<uintptr_t>(this))},
+ });
+
+ 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) {
+ 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;
+}
+
+vector<ProgramInfo> getProgramInfoVector(const vector<VirtualProgram>& vec, HalRevision halRev) {
+ vector<ProgramInfo> out;
+ out.reserve(vec.size());
+ for (auto&& program : vec) {
+ out.push_back(program.getProgramInfo(halRev));
+ }
+ return out;
+}
+
+} // namespace implementation
+} // namespace V1_2
+} // namespace broadcastradio
+} // namespace hardware
+} // namespace android
diff --git a/broadcastradio/1.2/default/VirtualProgram.h b/broadcastradio/1.2/default/VirtualProgram.h
new file mode 100644
index 0000000..5342c84
--- /dev/null
+++ b/broadcastradio/1.2/default/VirtualProgram.h
@@ -0,0 +1,55 @@
+/*
+ * 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_2_VIRTUALPROGRAM_H
+#define ANDROID_HARDWARE_BROADCASTRADIO_V1_2_VIRTUALPROGRAM_H
+
+#include <android/hardware/broadcastradio/1.2/types.h>
+#include <broadcastradio-utils/Utils.h>
+
+namespace android {
+namespace hardware {
+namespace broadcastradio {
+namespace V1_2 {
+namespace implementation {
+
+/**
+ * A radio program mock.
+ *
+ * This represents broadcast waves flying over the air,
+ * not an entry for a captured station in the radio tuner memory.
+ */
+struct VirtualProgram {
+ V1_1::ProgramSelector selector;
+
+ std::string programName = "";
+ std::string songArtist = "";
+ std::string songTitle = "";
+
+ V1_1::ProgramInfo getProgramInfo(utils::HalRevision halRev) const;
+
+ friend bool operator<(const VirtualProgram& lhs, const VirtualProgram& rhs);
+};
+
+std::vector<V1_1::ProgramInfo> getProgramInfoVector(const std::vector<VirtualProgram>& vec,
+ utils::HalRevision halRev);
+
+} // namespace implementation
+} // namespace V1_2
+} // namespace broadcastradio
+} // namespace hardware
+} // namespace android
+
+#endif // ANDROID_HARDWARE_BROADCASTRADIO_V1_2_VIRTUALPROGRAM_H
diff --git a/broadcastradio/1.2/default/VirtualRadio.cpp b/broadcastradio/1.2/default/VirtualRadio.cpp
new file mode 100644
index 0000000..867726d
--- /dev/null
+++ b/broadcastradio/1.2/default/VirtualRadio.cpp
@@ -0,0 +1,106 @@
+/*
+ * 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 "BroadcastRadioDefault.VirtualRadio"
+//#define LOG_NDEBUG 0
+
+#include "VirtualRadio.h"
+
+#include <broadcastradio-utils/Utils.h>
+#include <log/log.h>
+
+namespace android {
+namespace hardware {
+namespace broadcastradio {
+namespace V1_2 {
+namespace implementation {
+
+using V1_0::Band;
+using V1_0::Class;
+using V1_1::ProgramSelector;
+
+using std::lock_guard;
+using std::move;
+using std::mutex;
+using std::vector;
+
+using utils::make_selector;
+
+static const vector<VirtualProgram> gInitialFmPrograms{
+ {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"},
+};
+
+static VirtualRadio gEmptyRadio({});
+static VirtualRadio gFmRadio(gInitialFmPrograms);
+
+VirtualRadio::VirtualRadio(const vector<VirtualProgram> initialList) : mPrograms(initialList) {}
+
+vector<VirtualProgram> VirtualRadio::getProgramList() {
+ lock_guard<mutex> lk(mMut);
+ return mPrograms;
+}
+
+bool VirtualRadio::getProgram(const ProgramSelector& selector, VirtualProgram& programOut) {
+ lock_guard<mutex> lk(mMut);
+ for (auto&& program : mPrograms) {
+ if (utils::tunesTo(selector, program.selector)) {
+ programOut = program;
+ return true;
+ }
+ }
+ return false;
+}
+
+VirtualRadio& getRadio(V1_0::Class classId) {
+ switch (classId) {
+ case Class::AM_FM:
+ return getFmRadio();
+ case Class::SAT:
+ return getSatRadio();
+ case Class::DT:
+ return getDigitalRadio();
+ default:
+ ALOGE("Invalid class ID");
+ return gEmptyRadio;
+ }
+}
+
+VirtualRadio& getAmRadio() {
+ return gEmptyRadio;
+}
+
+VirtualRadio& getFmRadio() {
+ return gFmRadio;
+}
+
+VirtualRadio& getSatRadio() {
+ return gEmptyRadio;
+}
+
+VirtualRadio& getDigitalRadio() {
+ return gEmptyRadio;
+}
+
+} // namespace implementation
+} // namespace V1_2
+} // namespace broadcastradio
+} // namespace hardware
+} // namespace android
diff --git a/broadcastradio/1.2/default/VirtualRadio.h b/broadcastradio/1.2/default/VirtualRadio.h
new file mode 100644
index 0000000..8cfaefe
--- /dev/null
+++ b/broadcastradio/1.2/default/VirtualRadio.h
@@ -0,0 +1,80 @@
+/*
+ * 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_2_VIRTUALRADIO_H
+#define ANDROID_HARDWARE_BROADCASTRADIO_V1_2_VIRTUALRADIO_H
+
+#include "VirtualProgram.h"
+
+#include <mutex>
+#include <vector>
+
+namespace android {
+namespace hardware {
+namespace broadcastradio {
+namespace V1_2 {
+namespace implementation {
+
+/**
+ * A radio frequency space mock.
+ *
+ * This represents all broadcast waves in the air for a given radio technology,
+ * not a captured station list in the radio tuner memory.
+ *
+ * It's meant to abstract out radio content from default tuner implementation.
+ */
+class VirtualRadio {
+ public:
+ VirtualRadio(const std::vector<VirtualProgram> initialList);
+
+ std::vector<VirtualProgram> getProgramList();
+ bool getProgram(const V1_1::ProgramSelector& selector, VirtualProgram& program);
+
+ private:
+ std::mutex mMut;
+ std::vector<VirtualProgram> mPrograms;
+};
+
+/**
+ * Get virtual radio space for a given radio class.
+ *
+ * As a space, each virtual radio always exists. For example, DAB frequencies
+ * exists in US, but contains no programs.
+ *
+ * The lifetime of the virtual radio space is virtually infinite, but for the
+ * needs of default implementation, it's bound with the lifetime of default
+ * implementation process.
+ *
+ * Internally, it's a static object, so trying to access the reference during
+ * default implementation library unloading may result in segmentation fault.
+ * It's unlikely for testing purposes.
+ *
+ * @param classId A class of radio technology.
+ * @return A reference to virtual radio space for a given technology.
+ */
+VirtualRadio& getRadio(V1_0::Class classId);
+
+VirtualRadio& getAmRadio();
+VirtualRadio& getFmRadio();
+VirtualRadio& getSatRadio();
+VirtualRadio& getDigitalRadio();
+
+} // namespace implementation
+} // namespace V1_2
+} // namespace broadcastradio
+} // namespace hardware
+} // namespace android
+
+#endif // ANDROID_HARDWARE_BROADCASTRADIO_V1_2_VIRTUALRADIO_H
diff --git a/broadcastradio/1.2/default/android.hardware.broadcastradio@1.2-service.rc b/broadcastradio/1.2/default/android.hardware.broadcastradio@1.2-service.rc
new file mode 100644
index 0000000..3741f21
--- /dev/null
+++ b/broadcastradio/1.2/default/android.hardware.broadcastradio@1.2-service.rc
@@ -0,0 +1,4 @@
+service broadcastradio-hal /vendor/bin/hw/android.hardware.broadcastradio@1.2-service
+ class hal
+ user audioserver
+ group audio
diff --git a/broadcastradio/1.2/default/resources.h b/broadcastradio/1.2/default/resources.h
new file mode 100644
index 0000000..b383c27
--- /dev/null
+++ b/broadcastradio/1.2/default/resources.h
@@ -0,0 +1,46 @@
+/*
+ * 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_2_RESOURCES_H
+#define ANDROID_HARDWARE_BROADCASTRADIO_V1_2_RESOURCES_H
+
+namespace android {
+namespace hardware {
+namespace broadcastradio {
+namespace V1_2 {
+namespace implementation {
+namespace resources {
+
+constexpr int32_t demoPngId = 123456;
+constexpr uint8_t demoPng[] = {
+ 0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a, 0x00, 0x00, 0x00, 0x0d, 0x49, 0x48, 0x44,
+ 0x52, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x40, 0x08, 0x02, 0x00, 0x00, 0x00, 0x25,
+ 0x0b, 0xe6, 0x89, 0x00, 0x00, 0x00, 0x5d, 0x49, 0x44, 0x41, 0x54, 0x68, 0xde, 0xed, 0xd9,
+ 0xc1, 0x09, 0x00, 0x30, 0x08, 0x04, 0xc1, 0x33, 0xfd, 0xf7, 0x6c, 0x6a, 0xc8, 0x23, 0x04,
+ 0xc9, 0x6c, 0x01, 0xc2, 0x20, 0xbe, 0x4c, 0x86, 0x57, 0x49, 0xba, 0xfb, 0xd6, 0xf4, 0xba,
+ 0x3e, 0x7f, 0x4d, 0xdf, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0x8f, 0x00, 0xbd, 0xce, 0x7f,
+ 0xc0, 0x11, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe8, 0xb8, 0x0d, 0x32, 0xd4, 0x0c, 0x77, 0xbd,
+ 0xfb, 0xc1, 0xce, 0x00, 0x00, 0x00, 0x00, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82};
+
+} // namespace resources
+} // namespace implementation
+} // namespace V1_2
+} // namespace broadcastradio
+} // namespace hardware
+} // namespace android
+
+#endif // ANDROID_HARDWARE_BROADCASTRADIO_V1_2_RESOURCES_H
diff --git a/broadcastradio/1.2/default/service.cpp b/broadcastradio/1.2/default/service.cpp
new file mode 100644
index 0000000..ea86fba
--- /dev/null
+++ b/broadcastradio/1.2/default/service.cpp
@@ -0,0 +1,36 @@
+/*
+ * 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 "BroadcastRadioDefault.service"
+
+#include <android-base/logging.h>
+#include <hidl/HidlTransportSupport.h>
+
+#include "BroadcastRadioFactory.h"
+
+using android::hardware::configureRpcThreadpool;
+using android::hardware::joinRpcThreadpool;
+using android::hardware::broadcastradio::V1_2::implementation::BroadcastRadioFactory;
+
+int main(int /* argc */, char** /* argv */) {
+ configureRpcThreadpool(4, true);
+
+ BroadcastRadioFactory broadcastRadioFactory;
+ auto status = broadcastRadioFactory.registerAsService();
+ CHECK_EQ(status, android::OK) << "Failed to register Broadcast Radio HAL implementation";
+
+ joinRpcThreadpool();
+ return 1; // joinRpcThreadpool shouldn't exit
+}