summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorTomasz Wasilczyk2017-12-22 12:54:34 -0600
committerTomasz Wasilczyk2018-01-04 10:40:31 -0600
commitc71624f8ed57962fe85656aac5b687d8394dfdc0 (patch)
tree59612becad215197f91268ecc96cd3d3fbc0abd8 /broadcastradio
parent0914a946b23c604f3d437fd755d875306df139bb (diff)
downloadplatform-hardware-interfaces-c71624f8ed57962fe85656aac5b687d8394dfdc0.tar.gz
platform-hardware-interfaces-c71624f8ed57962fe85656aac5b687d8394dfdc0.tar.xz
platform-hardware-interfaces-c71624f8ed57962fe85656aac5b687d8394dfdc0.zip
Prepare a best-effort workaround for HD Radio station id abuse.
In theory, 32bit HD Radio station ID + subchannel index (parts of HD_STATION_ID_EXT) is a globally unique identifier. It allows broadcast radio framework to determine which programs are the same and allow the application to match entries from favourite list and the program list provided by the tuner. However, some broadcasters don't perform equipment setup correctly and don't set station ID. As a result, there are some stations with conflicting IDs. As a workaround to treat these stations separately in a given location, FM frequency was added as a part of HD_STATION_ID_EXT. This still doesn't solve the global uniqueness problem: user might save KCQW 105.5 (sid=0) in California, travel to Nevada and find KNAB 105.5 (sid=0). It turns out there is no reliable identifier that might identify the station globally. As a workaround, shortened station name is added for double-checking. This is a best-effort fix, so it's not required for such misbehaving stations to get correctly identified in every corner case. Bug: 69958777 Test: VTS Change-Id: Id11243096f1cde7fdda5cb70a7248d1831985cdd
Diffstat (limited to 'broadcastradio')
-rw-r--r--broadcastradio/2.0/types.hal25
-rw-r--r--broadcastradio/2.0/vts/functional/Android.bp3
-rw-r--r--broadcastradio/2.0/vts/functional/VtsHalBroadcastradioV2_0TargetTest.cpp57
-rw-r--r--broadcastradio/common/tests/Android.bp7
-rw-r--r--broadcastradio/common/tests/ProgramIdentifier_test.cpp40
-rw-r--r--broadcastradio/common/utils2x/Android.bp3
-rw-r--r--broadcastradio/common/utils2x/Utils.cpp43
-rw-r--r--broadcastradio/common/utils2x/include/broadcastradio-utils-2x/Utils.h6
8 files changed, 172 insertions, 12 deletions
diff --git a/broadcastradio/2.0/types.hal b/broadcastradio/2.0/types.hal
index 38a5709a..a4474c8c 100644
--- a/broadcastradio/2.0/types.hal
+++ b/broadcastradio/2.0/types.hal
@@ -466,7 +466,12 @@ enum IdentifierType : uint32_t {
466 * Consists of (from the LSB): 466 * Consists of (from the LSB):
467 * - 32bit: Station ID number; 467 * - 32bit: Station ID number;
468 * - 4bit: HD Radio subchannel; 468 * - 4bit: HD Radio subchannel;
469 * - 18bit: AMFM_FREQUENCY. // TODO(b/69958777): is it necessary? 469 * - 18bit: AMFM_FREQUENCY.
470 *
471 * While station ID number should be unique globally, it sometimes get
472 * abused by broadcasters (i.e. not being set at all). To ensure local
473 * uniqueness, AMFM_FREQUENCY was added here. Global uniqueness is
474 * a best-effort - see HD_STATION_NAME.
470 * 475 *
471 * HD Radio subchannel is a value in range 0-7. 476 * HD Radio subchannel is a value in range 0-7.
472 * This index is 0-based (where 0 is MPS and 1..7 are SPS), 477 * This index is 0-based (where 0 is MPS and 1..7 are SPS),
@@ -478,6 +483,22 @@ enum IdentifierType : uint32_t {
478 HD_STATION_ID_EXT, 483 HD_STATION_ID_EXT,
479 484
480 /** 485 /**
486 * 64bit additional identifier for HD Radio.
487 *
488 * Due to Station ID abuse, some HD_STATION_ID_EXT identifiers may be not
489 * globally unique. To provide a best-effort solution, a short version of
490 * station name may be carried as additional identifier and may be used
491 * by the tuner hardware to double-check tuning.
492 *
493 * The name is limited to the first 8 A-Z0-9 characters (lowercase letters
494 * must be converted to uppercase). Encoded in little-endian ASCII:
495 * the first character of the name is the LSB.
496 *
497 * For example: "Abc" is encoded as 0x434241.
498 */
499 HD_STATION_NAME,
500
501 /**
481 * 28bit compound primary identifier for Digital Audio Broadcasting. 502 * 28bit compound primary identifier for Digital Audio Broadcasting.
482 * 503 *
483 * Consists of (from the LSB): 504 * Consists of (from the LSB):
@@ -492,7 +513,7 @@ enum IdentifierType : uint32_t {
492 * The remaining bits should be set to zeros when writing on the chip side 513 * The remaining bits should be set to zeros when writing on the chip side
493 * and ignored when read. 514 * and ignored when read.
494 */ 515 */
495 DAB_SID_EXT = HD_STATION_ID_EXT + 2, 516 DAB_SID_EXT,
496 517
497 /** 16bit */ 518 /** 16bit */
498 DAB_ENSEMBLE, 519 DAB_ENSEMBLE,
diff --git a/broadcastradio/2.0/vts/functional/Android.bp b/broadcastradio/2.0/vts/functional/Android.bp
index 6017b152..6940bca4 100644
--- a/broadcastradio/2.0/vts/functional/Android.bp
+++ b/broadcastradio/2.0/vts/functional/Android.bp
@@ -17,6 +17,9 @@
17cc_test { 17cc_test {
18 name: "VtsHalBroadcastradioV2_0TargetTest", 18 name: "VtsHalBroadcastradioV2_0TargetTest",
19 defaults: ["VtsHalTargetTestDefaults"], 19 defaults: ["VtsHalTargetTestDefaults"],
20 cppflags: [
21 "-std=c++1z",
22 ],
20 srcs: ["VtsHalBroadcastradioV2_0TargetTest.cpp"], 23 srcs: ["VtsHalBroadcastradioV2_0TargetTest.cpp"],
21 static_libs: [ 24 static_libs: [
22 "android.hardware.broadcastradio@2.0", 25 "android.hardware.broadcastradio@2.0",
diff --git a/broadcastradio/2.0/vts/functional/VtsHalBroadcastradioV2_0TargetTest.cpp b/broadcastradio/2.0/vts/functional/VtsHalBroadcastradioV2_0TargetTest.cpp
index 87ac9341..151e089a 100644
--- a/broadcastradio/2.0/vts/functional/VtsHalBroadcastradioV2_0TargetTest.cpp
+++ b/broadcastradio/2.0/vts/functional/VtsHalBroadcastradioV2_0TargetTest.cpp
@@ -30,6 +30,7 @@
30#include <gmock/gmock.h> 30#include <gmock/gmock.h>
31 31
32#include <chrono> 32#include <chrono>
33#include <optional>
33#include <regex> 34#include <regex>
34 35
35namespace android { 36namespace android {
@@ -96,6 +97,7 @@ class BroadcastRadioHalTest : public ::testing::VtsHalHidlTargetTestBase {
96 97
97 bool openSession(); 98 bool openSession();
98 bool getAmFmRegionConfig(bool full, AmFmRegionConfig* config); 99 bool getAmFmRegionConfig(bool full, AmFmRegionConfig* config);
100 std::optional<utils::ProgramInfoSet> getProgramList();
99 101
100 sp<IBroadcastRadio> mModule; 102 sp<IBroadcastRadio> mModule;
101 Properties mProperties; 103 Properties mProperties;
@@ -178,6 +180,25 @@ bool BroadcastRadioHalTest::getAmFmRegionConfig(bool full, AmFmRegionConfig* con
178 return halResult == Result::OK; 180 return halResult == Result::OK;
179} 181}
180 182
183std::optional<utils::ProgramInfoSet> BroadcastRadioHalTest::getProgramList() {
184 EXPECT_TIMEOUT_CALL(*mCallback, onProgramListReady).Times(AnyNumber());
185
186 auto startResult = mSession->startProgramListUpdates({});
187 if (startResult == Result::NOT_SUPPORTED) {
188 printSkipped("Program list not supported");
189 return nullopt;
190 }
191 EXPECT_EQ(Result::OK, startResult);
192 if (startResult != Result::OK) return nullopt;
193
194 EXPECT_TIMEOUT_CALL_WAIT(*mCallback, onProgramListReady, timeout::programListScan);
195
196 auto stopResult = mSession->stopProgramListUpdates();
197 EXPECT_TRUE(stopResult.isOk());
198
199 return mCallback->mProgramList;
200}
201
181/** 202/**
182 * Test session opening. 203 * Test session opening.
183 * 204 *
@@ -645,19 +666,35 @@ TEST_F(BroadcastRadioHalTest, SetConfigFlags) {
645TEST_F(BroadcastRadioHalTest, GetProgramList) { 666TEST_F(BroadcastRadioHalTest, GetProgramList) {
646 ASSERT_TRUE(openSession()); 667 ASSERT_TRUE(openSession());
647 668
648 EXPECT_TIMEOUT_CALL(*mCallback, onProgramListReady).Times(AnyNumber()); 669 getProgramList();
670}
649 671
650 auto startResult = mSession->startProgramListUpdates({}); 672/**
651 if (startResult == Result::NOT_SUPPORTED) { 673 * Test HD_STATION_NAME correctness.
652 printSkipped("Program list not supported"); 674 *
653 return; 675 * Verifies that if a program on the list contains HD_STATION_NAME identifier:
654 } 676 * - the program provides station name in its metadata;
655 ASSERT_EQ(Result::OK, startResult); 677 * - the identifier matches the name;
678 * - there is only one identifier of that type.
679 */
680TEST_F(BroadcastRadioHalTest, HdRadioStationNameId) {
681 ASSERT_TRUE(openSession());
656 682
657 EXPECT_TIMEOUT_CALL_WAIT(*mCallback, onProgramListReady, timeout::programListScan); 683 auto list = getProgramList();
684 if (!list) return;
658 685
659 auto stopResult = mSession->stopProgramListUpdates(); 686 for (auto&& program : *list) {
660 EXPECT_TRUE(stopResult.isOk()); 687 auto nameIds = utils::getAllIds(program.selector, IdentifierType::HD_STATION_NAME);
688 EXPECT_LE(nameIds.size(), 1u);
689 if (nameIds.size() == 0) continue;
690
691 auto name = utils::getMetadataString(program, MetadataKey::PROGRAM_NAME);
692 if (!name) name = utils::getMetadataString(program, MetadataKey::RDS_PS);
693 ASSERT_TRUE(name.has_value());
694
695 auto expectedId = utils::make_hdradio_station_name(*name);
696 EXPECT_EQ(expectedId.value, nameIds[0]);
697 }
661} 698}
662 699
663// TODO(b/70939328): test ProgramInfo's currentlyTunedId and 700// TODO(b/70939328): test ProgramInfo's currentlyTunedId and
diff --git a/broadcastradio/common/tests/Android.bp b/broadcastradio/common/tests/Android.bp
index 512c02e4..f6a3b6f4 100644
--- a/broadcastradio/common/tests/Android.bp
+++ b/broadcastradio/common/tests/Android.bp
@@ -22,6 +22,9 @@ cc_test {
22 "-Wextra", 22 "-Wextra",
23 "-Werror", 23 "-Werror",
24 ], 24 ],
25 cppflags: [
26 "-std=c++1z",
27 ],
25 srcs: [ 28 srcs: [
26 "CommonXX_test.cpp", 29 "CommonXX_test.cpp",
27 ], 30 ],
@@ -43,8 +46,12 @@ cc_test {
43 "-Wextra", 46 "-Wextra",
44 "-Werror", 47 "-Werror",
45 ], 48 ],
49 cppflags: [
50 "-std=c++1z",
51 ],
46 srcs: [ 52 srcs: [
47 "IdentifierIterator_test.cpp", 53 "IdentifierIterator_test.cpp",
54 "ProgramIdentifier_test.cpp",
48 ], 55 ],
49 static_libs: [ 56 static_libs: [
50 "android.hardware.broadcastradio@common-utils-2x-lib", 57 "android.hardware.broadcastradio@common-utils-2x-lib",
diff --git a/broadcastradio/common/tests/ProgramIdentifier_test.cpp b/broadcastradio/common/tests/ProgramIdentifier_test.cpp
new file mode 100644
index 00000000..51ad0145
--- /dev/null
+++ b/broadcastradio/common/tests/ProgramIdentifier_test.cpp
@@ -0,0 +1,40 @@
1/*
2 * Copyright (C) 2017 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#include <broadcastradio-utils-2x/Utils.h>
18#include <gtest/gtest.h>
19
20#include <optional>
21
22namespace {
23
24namespace utils = android::hardware::broadcastradio::utils;
25
26TEST(ProgramIdentifierTest, hdRadioStationName) {
27 auto verify = [](std::string name, uint64_t nameId) {
28 auto id = utils::make_hdradio_station_name(name);
29 EXPECT_EQ(nameId, id.value) << "Failed to convert '" << name << "'";
30 };
31
32 verify("", 0);
33 verify("Abc", 0x434241);
34 verify("Some Station 1", 0x54415453454d4f53);
35 verify("Station1", 0x314e4f4954415453);
36 verify("!@#$%^&*()_+", 0);
37 verify("-=[]{};':\"0", 0x30);
38}
39
40} // anonymous namespace
diff --git a/broadcastradio/common/utils2x/Android.bp b/broadcastradio/common/utils2x/Android.bp
index c6b94afb..aab94f2a 100644
--- a/broadcastradio/common/utils2x/Android.bp
+++ b/broadcastradio/common/utils2x/Android.bp
@@ -23,6 +23,9 @@ cc_library_static {
23 "-Wextra", 23 "-Wextra",
24 "-Werror", 24 "-Werror",
25 ], 25 ],
26 cppflags: [
27 "-std=c++1z",
28 ],
26 srcs: [ 29 srcs: [
27 "Utils.cpp", 30 "Utils.cpp",
28 ], 31 ],
diff --git a/broadcastradio/common/utils2x/Utils.cpp b/broadcastradio/common/utils2x/Utils.cpp
index d825a7a0..6fe95549 100644
--- a/broadcastradio/common/utils2x/Utils.cpp
+++ b/broadcastradio/common/utils2x/Utils.cpp
@@ -245,6 +245,15 @@ bool isValid(const ProgramIdentifier& id) {
245 expect(freq < 10000000u, "f < 10GHz"); 245 expect(freq < 10000000u, "f < 10GHz");
246 break; 246 break;
247 } 247 }
248 case IdentifierType::HD_STATION_NAME: {
249 while (val > 0) {
250 auto ch = static_cast<char>(val & 0xFF);
251 val >>= 8;
252 expect((ch >= '0' && ch <= '9') || (ch >= 'A' && ch <= 'Z'),
253 "HD_STATION_NAME does not match [A-Z0-9]+");
254 }
255 break;
256 }
248 case IdentifierType::DAB_SID_EXT: { 257 case IdentifierType::DAB_SID_EXT: {
249 auto sid = val & 0xFFFF; // 16bit 258 auto sid = val & 0xFFFF; // 16bit
250 val >>= 16; 259 val >>= 16;
@@ -367,6 +376,40 @@ void updateProgramList(ProgramInfoSet& list, const ProgramListChunk& chunk) {
367 } 376 }
368} 377}
369 378
379std::optional<std::string> getMetadataString(const V2_0::ProgramInfo& info,
380 const V2_0::MetadataKey key) {
381 auto isKey = [key](const V2_0::Metadata& item) {
382 return static_cast<V2_0::MetadataKey>(item.key) == key;
383 };
384
385 auto it = std::find_if(info.metadata.begin(), info.metadata.end(), isKey);
386 if (it == info.metadata.end()) return std::nullopt;
387
388 return it->stringValue;
389}
390
391V2_0::ProgramIdentifier make_hdradio_station_name(const std::string& name) {
392 constexpr size_t maxlen = 8;
393
394 std::string shortName;
395 shortName.reserve(maxlen);
396
397 auto&& loc = std::locale::classic();
398 for (char ch : name) {
399 if (!std::isalnum(ch, loc)) continue;
400 shortName.push_back(std::toupper(ch, loc));
401 if (shortName.length() >= maxlen) break;
402 }
403
404 uint64_t val = 0;
405 for (auto rit = shortName.rbegin(); rit != shortName.rend(); ++rit) {
406 val <<= 8;
407 val |= static_cast<uint8_t>(*rit);
408 }
409
410 return make_identifier(IdentifierType::HD_STATION_NAME, val);
411}
412
370} // namespace utils 413} // namespace utils
371} // namespace broadcastradio 414} // namespace broadcastradio
372} // namespace hardware 415} // namespace hardware
diff --git a/broadcastradio/common/utils2x/include/broadcastradio-utils-2x/Utils.h b/broadcastradio/common/utils2x/include/broadcastradio-utils-2x/Utils.h
index e3134f7b..5e519416 100644
--- a/broadcastradio/common/utils2x/include/broadcastradio-utils-2x/Utils.h
+++ b/broadcastradio/common/utils2x/include/broadcastradio-utils-2x/Utils.h
@@ -18,6 +18,7 @@
18 18
19#include <android/hardware/broadcastradio/2.0/types.h> 19#include <android/hardware/broadcastradio/2.0/types.h>
20#include <chrono> 20#include <chrono>
21#include <optional>
21#include <queue> 22#include <queue>
22#include <thread> 23#include <thread>
23#include <unordered_set> 24#include <unordered_set>
@@ -146,6 +147,11 @@ typedef std::unordered_set<V2_0::ProgramInfo, ProgramInfoHasher, ProgramInfoKeyE
146 147
147void updateProgramList(ProgramInfoSet& list, const V2_0::ProgramListChunk& chunk); 148void updateProgramList(ProgramInfoSet& list, const V2_0::ProgramListChunk& chunk);
148 149
150std::optional<std::string> getMetadataString(const V2_0::ProgramInfo& info,
151 const V2_0::MetadataKey key);
152
153V2_0::ProgramIdentifier make_hdradio_station_name(const std::string& name);
154
149} // namespace utils 155} // namespace utils
150} // namespace broadcastradio 156} // namespace broadcastradio
151} // namespace hardware 157} // namespace hardware