Branch out Broadcast Radio 1.2 HAL.
Bug: 62945293
Change-Id: Ic84f9fe6c17040053257085801208ed527fd07ab
Fixes: 64115813
Test: instrumentation, VTS
diff --git a/broadcastradio/common/utils/Android.bp b/broadcastradio/common/utils/Android.bp
new file mode 100644
index 0000000..d8bd125
--- /dev/null
+++ b/broadcastradio/common/utils/Android.bp
@@ -0,0 +1,34 @@
+//
+// 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@common-utils-lib",
+ vendor_available: true,
+ relative_install_path: "hw",
+ cflags: [
+ "-Wall",
+ "-Wextra",
+ "-Werror",
+ ],
+ srcs: [
+ "Utils.cpp",
+ "WorkerThread.cpp",
+ ],
+ export_include_dirs: ["include"],
+ shared_libs: [
+ "android.hardware.broadcastradio@1.1",
+ ],
+}
diff --git a/broadcastradio/common/utils/OWNERS b/broadcastradio/common/utils/OWNERS
new file mode 100644
index 0000000..136b607
--- /dev/null
+++ b/broadcastradio/common/utils/OWNERS
@@ -0,0 +1,3 @@
+# Automotive team
+egranata@google.com
+twasilczyk@google.com
diff --git a/broadcastradio/common/utils/Utils.cpp b/broadcastradio/common/utils/Utils.cpp
new file mode 100644
index 0000000..bdaf8e8
--- /dev/null
+++ b/broadcastradio/common/utils/Utils.cpp
@@ -0,0 +1,236 @@
+/*
+ * 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.utils"
+//#define LOG_NDEBUG 0
+
+#include <broadcastradio-utils/Utils.h>
+
+#include <log/log.h>
+
+namespace android {
+namespace hardware {
+namespace broadcastradio {
+namespace utils {
+
+using V1_0::Band;
+using V1_1::IdentifierType;
+using V1_1::ProgramIdentifier;
+using V1_1::ProgramSelector;
+using V1_1::ProgramType;
+
+static bool isCompatibleProgramType(const uint32_t ia, const uint32_t ib) {
+ auto a = static_cast<ProgramType>(ia);
+ auto b = static_cast<ProgramType>(ib);
+
+ if (a == b) return true;
+ if (a == ProgramType::AM && b == ProgramType::AM_HD) return true;
+ if (a == ProgramType::AM_HD && b == ProgramType::AM) return true;
+ if (a == ProgramType::FM && b == ProgramType::FM_HD) return true;
+ if (a == ProgramType::FM_HD && b == ProgramType::FM) return true;
+ return false;
+}
+
+static bool bothHaveId(const ProgramSelector& a, const ProgramSelector& b,
+ const IdentifierType type) {
+ return hasId(a, type) && hasId(b, type);
+}
+
+static bool anyHaveId(const ProgramSelector& a, const ProgramSelector& b,
+ const IdentifierType type) {
+ return hasId(a, type) || hasId(b, type);
+}
+
+static bool haveEqualIds(const ProgramSelector& a, const ProgramSelector& b,
+ const IdentifierType type) {
+ if (!bothHaveId(a, b, type)) return false;
+ /* We should check all Ids of a given type (ie. other AF),
+ * but it doesn't matter for default implementation.
+ */
+ auto aId = getId(a, type);
+ auto bId = getId(b, type);
+ return aId == bId;
+}
+
+bool tunesTo(const ProgramSelector& a, const ProgramSelector& b) {
+ if (!isCompatibleProgramType(a.programType, b.programType)) return false;
+
+ auto type = getType(a);
+
+ switch (type) {
+ case ProgramType::AM:
+ case ProgramType::AM_HD:
+ case ProgramType::FM:
+ case ProgramType::FM_HD:
+ if (haveEqualIds(a, b, IdentifierType::HD_STATION_ID_EXT)) return true;
+
+ // if HD Radio subchannel is specified, it must match
+ if (anyHaveId(a, b, IdentifierType::HD_SUBCHANNEL)) {
+ // missing subchannel (analog) is an equivalent of first subchannel (MPS)
+ auto aCh = getId(a, IdentifierType::HD_SUBCHANNEL, 0);
+ auto bCh = getId(b, IdentifierType::HD_SUBCHANNEL, 0);
+ if (aCh != bCh) return false;
+ }
+
+ if (haveEqualIds(a, b, IdentifierType::RDS_PI)) return true;
+
+ return haveEqualIds(a, b, IdentifierType::AMFM_FREQUENCY);
+ case ProgramType::DAB:
+ return haveEqualIds(a, b, IdentifierType::DAB_SIDECC);
+ case ProgramType::DRMO:
+ return haveEqualIds(a, b, IdentifierType::DRMO_SERVICE_ID);
+ case ProgramType::SXM:
+ if (anyHaveId(a, b, IdentifierType::SXM_SERVICE_ID)) {
+ return haveEqualIds(a, b, IdentifierType::SXM_SERVICE_ID);
+ }
+ return haveEqualIds(a, b, IdentifierType::SXM_CHANNEL);
+ default: // includes all vendor types
+ ALOGW("Unsupported program type: %s", toString(type).c_str());
+ return false;
+ }
+}
+
+ProgramType getType(const ProgramSelector& sel) {
+ return static_cast<ProgramType>(sel.programType);
+}
+
+bool isAmFm(const ProgramType type) {
+ switch (type) {
+ case ProgramType::AM:
+ case ProgramType::FM:
+ case ProgramType::AM_HD:
+ case ProgramType::FM_HD:
+ return true;
+ default:
+ return false;
+ }
+}
+
+bool isAm(const Band band) {
+ return band == Band::AM || band == Band::AM_HD;
+}
+
+bool isFm(const Band band) {
+ return band == Band::FM || band == Band::FM_HD;
+}
+
+bool hasId(const ProgramSelector& sel, const IdentifierType type) {
+ auto itype = static_cast<uint32_t>(type);
+ if (sel.primaryId.type == itype) return true;
+ // not optimal, but we don't care in default impl
+ for (auto&& id : sel.secondaryIds) {
+ if (id.type == itype) return true;
+ }
+ return false;
+}
+
+uint64_t getId(const ProgramSelector& sel, const IdentifierType type) {
+ auto itype = static_cast<uint32_t>(type);
+ if (sel.primaryId.type == itype) return sel.primaryId.value;
+ // not optimal, but we don't care in default impl
+ for (auto&& id : sel.secondaryIds) {
+ if (id.type == itype) return id.value;
+ }
+ ALOGW("Identifier %s not found", toString(type).c_str());
+ return 0;
+}
+
+uint64_t getId(const ProgramSelector& sel, const IdentifierType type, uint64_t defval) {
+ if (!hasId(sel, type)) return defval;
+ return getId(sel, type);
+}
+
+ProgramSelector make_selector(Band band, uint32_t channel, uint32_t subChannel) {
+ ProgramSelector sel = {};
+
+ ALOGW_IF((subChannel > 0) && (band == Band::AM || band == Band::FM),
+ "got subChannel for non-HD AM/FM");
+
+ // we can't use ProgramType::AM_HD or FM_HD, because we don't know HD station ID
+ ProgramType type;
+ if (isAm(band)) {
+ type = ProgramType::AM;
+ } else if (isFm(band)) {
+ type = ProgramType::FM;
+ } else {
+ LOG_ALWAYS_FATAL("Unsupported band: %s", toString(band).c_str());
+ }
+
+ sel.programType = static_cast<uint32_t>(type);
+ sel.primaryId.type = static_cast<uint32_t>(IdentifierType::AMFM_FREQUENCY);
+ sel.primaryId.value = channel;
+ if (subChannel > 0) {
+ /* stating sub channel for AM/FM channel does not give any guarantees,
+ * but we can't do much more without HD station ID
+ *
+ * The legacy APIs had 1-based subChannels, while ProgramSelector is 0-based.
+ */
+ sel.secondaryIds = hidl_vec<ProgramIdentifier>{
+ {static_cast<uint32_t>(IdentifierType::HD_SUBCHANNEL), subChannel - 1},
+ };
+ }
+
+ return sel;
+}
+
+bool getLegacyChannel(const ProgramSelector& sel, uint32_t* channelOut, uint32_t* subChannelOut) {
+ if (channelOut) *channelOut = 0;
+ if (subChannelOut) *subChannelOut = 0;
+ if (isAmFm(getType(sel))) {
+ if (channelOut) *channelOut = getId(sel, IdentifierType::AMFM_FREQUENCY);
+ if (subChannelOut && hasId(sel, IdentifierType::HD_SUBCHANNEL)) {
+ // The legacy APIs had 1-based subChannels, while ProgramSelector is 0-based.
+ *subChannelOut = getId(sel, IdentifierType::HD_SUBCHANNEL) + 1;
+ }
+ return true;
+ }
+ return false;
+}
+
+bool isDigital(const ProgramSelector& sel) {
+ switch (getType(sel)) {
+ case ProgramType::AM:
+ case ProgramType::FM:
+ return false;
+ default:
+ // VENDOR might not be digital, but it doesn't matter for default impl.
+ return true;
+ }
+}
+
+} // namespace utils
+
+namespace V1_0 {
+
+bool operator==(const BandConfig& l, const BandConfig& r) {
+ if (l.type != r.type) return false;
+ if (l.antennaConnected != r.antennaConnected) return false;
+ if (l.lowerLimit != r.lowerLimit) return false;
+ if (l.upperLimit != r.upperLimit) return false;
+ if (l.spacings != r.spacings) return false;
+ if (utils::isAm(l.type)) {
+ return l.ext.am == r.ext.am;
+ } else if (utils::isFm(l.type)) {
+ return l.ext.fm == r.ext.fm;
+ } else {
+ ALOGW("Unsupported band config type: %s", toString(l.type).c_str());
+ return false;
+ }
+}
+
+} // namespace V1_0
+} // namespace broadcastradio
+} // namespace hardware
+} // namespace android
diff --git a/broadcastradio/common/utils/WorkerThread.cpp b/broadcastradio/common/utils/WorkerThread.cpp
new file mode 100644
index 0000000..bfcbb39
--- /dev/null
+++ b/broadcastradio/common/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 <broadcastradio-utils/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/common/utils/include/broadcastradio-utils/Utils.h b/broadcastradio/common/utils/include/broadcastradio-utils/Utils.h
new file mode 100644
index 0000000..b07ce79
--- /dev/null
+++ b/broadcastradio/common/utils/include/broadcastradio-utils/Utils.h
@@ -0,0 +1,88 @@
+/*
+ * 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_COMMON_UTILS_H
+#define ANDROID_HARDWARE_BROADCASTRADIO_COMMON_UTILS_H
+
+#include <android/hardware/broadcastradio/1.1/types.h>
+#include <chrono>
+#include <queue>
+#include <thread>
+
+namespace android {
+namespace hardware {
+namespace broadcastradio {
+namespace utils {
+
+enum class HalRevision : uint32_t {
+ V1_0 = 1,
+ V1_1,
+ V1_2,
+};
+
+/**
+ * Checks, if {@code pointer} tunes to {@channel}.
+ *
+ * For example, having a channel {AMFM_FREQUENCY = 103.3}:
+ * - selector {AMFM_FREQUENCY = 103.3, HD_SUBCHANNEL = 0} can tune to this channel;
+ * - selector {AMFM_FREQUENCY = 103.3, HD_SUBCHANNEL = 1} can't.
+ *
+ * @param pointer selector we're trying to match against channel.
+ * @param channel existing channel.
+ */
+bool tunesTo(const V1_1::ProgramSelector& pointer, const V1_1::ProgramSelector& channel);
+
+V1_1::ProgramType getType(const V1_1::ProgramSelector& sel);
+bool isAmFm(const V1_1::ProgramType type);
+
+bool isAm(const V1_0::Band band);
+bool isFm(const V1_0::Band band);
+
+bool hasId(const V1_1::ProgramSelector& sel, const V1_1::IdentifierType type);
+
+/**
+ * Returns ID (either primary or secondary) for a given program selector.
+ *
+ * If the selector does not contain given type, returns 0 and emits a warning.
+ */
+uint64_t getId(const V1_1::ProgramSelector& sel, const V1_1::IdentifierType type);
+
+/**
+ * Returns ID (either primary or secondary) for a given program selector.
+ *
+ * If the selector does not contain given type, returns default value.
+ */
+uint64_t getId(const V1_1::ProgramSelector& sel, const V1_1::IdentifierType type, uint64_t defval);
+
+V1_1::ProgramSelector make_selector(V1_0::Band band, uint32_t channel, uint32_t subChannel = 0);
+
+bool getLegacyChannel(const V1_1::ProgramSelector& sel,
+ uint32_t* channelOut, uint32_t* subChannelOut);
+
+bool isDigital(const V1_1::ProgramSelector& sel);
+
+} // namespace utils
+
+namespace V1_0 {
+
+bool operator==(const BandConfig& l, const BandConfig& r);
+
+} // namespace V1_0
+
+} // namespace broadcastradio
+} // namespace hardware
+} // namespace android
+
+#endif // ANDROID_HARDWARE_BROADCASTRADIO_COMMON_UTILS_H
diff --git a/broadcastradio/common/utils/include/broadcastradio-utils/WorkerThread.h b/broadcastradio/common/utils/include/broadcastradio-utils/WorkerThread.h
new file mode 100644
index 0000000..62bede6
--- /dev/null
+++ b/broadcastradio/common/utils/include/broadcastradio-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_COMMON_WORKERTHREAD_H
+#define ANDROID_HARDWARE_BROADCASTRADIO_COMMON_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_COMMON_WORKERTHREAD_H