Merge changes from topic "remoteaccess_hal"
* changes:
Implement receiving remote task.
Implement the GrpcWakeupClient.
Define remoteaccess grpc HAL.
diff --git a/audio/common/all-versions/default/tests/hidlutils_tests.cpp b/audio/common/all-versions/default/tests/hidlutils_tests.cpp
index c295aaa..93688fc 100644
--- a/audio/common/all-versions/default/tests/hidlutils_tests.cpp
+++ b/audio/common/all-versions/default/tests/hidlutils_tests.cpp
@@ -17,7 +17,6 @@
#include <array>
#include <string>
-#include <android-base/test_utils.h>
#include <gtest/gtest.h>
#define LOG_TAG "HidlUtils_Test"
@@ -1125,12 +1124,15 @@
TYPED_TEST_SUITE(FilterTest, FilterTestTypeParams);
TYPED_TEST(FilterTest, FilterOutNonVendorTags) {
- SKIP_WITH_HWASAN; // b/230535046
TypeParam emptyTags;
EXPECT_EQ(emptyTags, HidlUtils::filterOutNonVendorTags(emptyTags));
- TypeParam allVendorTags = {{"VX_GOOGLE_VR_42", "VX_GOOGLE_1E100"}};
- EXPECT_EQ(allVendorTags, HidlUtils::filterOutNonVendorTags(allVendorTags));
+ // b/248421569, allocate two vendor tags at a time can run out of memory
+ // TypeParam allVendorTags = {{"VX_GOOGLE_VR_42", "VX_GOOGLE_1E100"}};
+ TypeParam allVendorTags1 = {{"VX_GOOGLE_VR_42"}};
+ EXPECT_EQ(allVendorTags1, HidlUtils::filterOutNonVendorTags(allVendorTags1));
+ TypeParam allVendorTags2 = {{"VX_GOOGLE_1E100"}};
+ EXPECT_EQ(allVendorTags2, HidlUtils::filterOutNonVendorTags(allVendorTags2));
TypeParam oneVendorTag = {{"", "VX_GOOGLE_VR", "random_string"}};
TypeParam oneVendorTagOnly = HidlUtils::filterOutNonVendorTags(oneVendorTag);
diff --git a/automotive/vehicle/aidl/impl/default_config/config/DefaultProperties.json b/automotive/vehicle/aidl/impl/default_config/config/DefaultProperties.json
index 0c949d6..b6811b6 100644
--- a/automotive/vehicle/aidl/impl/default_config/config/DefaultProperties.json
+++ b/automotive/vehicle/aidl/impl/default_config/config/DefaultProperties.json
@@ -110,6 +110,16 @@
"minSampleRate": 1.0
},
{
+ "property": "VehicleProperty::PERF_VEHICLE_SPEED_DISPLAY",
+ "defaultValue": {
+ "floatValues": [
+ 0.0
+ ]
+ },
+ "maxSampleRate": 10.0,
+ "minSampleRate": 1.0
+ },
+ {
"property": "VehicleProperty::VEHICLE_SPEED_DISPLAY_UNITS",
"defaultValue": {
"int32Values": [
diff --git a/automotive/vehicle/vts/Android.bp b/automotive/vehicle/vts/Android.bp
index 1cfd542..736787b 100644
--- a/automotive/vehicle/vts/Android.bp
+++ b/automotive/vehicle/vts/Android.bp
@@ -45,6 +45,7 @@
"general-tests",
"vts",
"automotive-tests",
+ "automotive-general-tests",
],
require_root: true,
}
diff --git a/bluetooth/audio/aidl/vts/VtsHalBluetoothAudioTargetTest.cpp b/bluetooth/audio/aidl/vts/VtsHalBluetoothAudioTargetTest.cpp
index ebd728d..e9b74b7 100644
--- a/bluetooth/audio/aidl/vts/VtsHalBluetoothAudioTargetTest.cpp
+++ b/bluetooth/audio/aidl/vts/VtsHalBluetoothAudioTargetTest.cpp
@@ -23,6 +23,7 @@
#include <android/binder_process.h>
#include <binder/IServiceManager.h>
#include <binder/ProcessState.h>
+#include <cutils/properties.h>
#include <fmq/AidlMessageQueue.h>
#include <cstdint>
@@ -248,7 +249,8 @@
case SessionType::LE_AUDIO_HARDWARE_OFFLOAD_ENCODING_DATAPATH:
case SessionType::LE_AUDIO_HARDWARE_OFFLOAD_DECODING_DATAPATH:
case SessionType::LE_AUDIO_BROADCAST_HARDWARE_OFFLOAD_ENCODING_DATAPATH: {
- ASSERT_FALSE(temp_provider_capabilities_.empty());
+ // empty capability means offload is unsupported since capabilities are
+ // not hardcoded
for (auto audio_capability : temp_provider_capabilities_) {
ASSERT_EQ(audio_capability.getTag(),
AudioCapabilities::leAudioCapabilities);
@@ -513,8 +515,9 @@
for (auto channel_mode : opus_capability->channelMode) {
OpusConfiguration opus_data{
.samplingFrequencyHz = samplingFrequencyHz,
+ .frameDurationUs = frameDurationUs,
.channelMode = channel_mode,
- .frameDurationUs = frameDurationUs};
+ };
opus_codec_specifics.push_back(
CodecConfiguration::CodecSpecific(opus_data));
}
@@ -623,8 +626,8 @@
for (auto channel_mode : a2dp_channel_modes) {
PcmConfiguration pcm_config{
.sampleRateHz = sample_rate,
- .bitsPerSample = bits_per_sample,
.channelMode = channel_mode,
+ .bitsPerSample = bits_per_sample,
};
bool is_codec_config_valid = IsPcmConfigSupported(pcm_config);
DataMQDesc mq_desc;
@@ -937,8 +940,8 @@
for (auto channel_mode : hearing_aid_channel_modes_) {
PcmConfiguration pcm_config{
.sampleRateHz = sample_rate,
- .bitsPerSample = bits_per_sample,
.channelMode = channel_mode,
+ .bitsPerSample = bits_per_sample,
};
bool is_codec_config_valid = IsPcmConfigSupported(pcm_config);
DataMQDesc mq_desc;
@@ -1008,8 +1011,8 @@
for (auto data_interval_us : le_audio_output_data_interval_us_) {
PcmConfiguration pcm_config{
.sampleRateHz = sample_rate,
- .bitsPerSample = bits_per_sample,
.channelMode = channel_mode,
+ .bitsPerSample = bits_per_sample,
.dataIntervalUs = data_interval_us,
};
bool is_codec_config_valid =
@@ -1081,8 +1084,8 @@
for (auto data_interval_us : le_audio_input_data_interval_us_) {
PcmConfiguration pcm_config{
.sampleRateHz = sample_rate,
- .bitsPerSample = bits_per_sample,
.channelMode = channel_mode,
+ .bitsPerSample = bits_per_sample,
.dataIntervalUs = data_interval_us,
};
bool is_codec_config_valid =
@@ -1144,7 +1147,7 @@
bool supported) {
std::vector<Lc3Configuration> le_audio_codec_configs;
if (!supported) {
- Lc3Configuration lc3_config{.samplingFrequencyHz = 0, .pcmBitDepth = 0};
+ Lc3Configuration lc3_config{.pcmBitDepth = 0, .samplingFrequencyHz = 0};
le_audio_codec_configs.push_back(lc3_config);
return le_audio_codec_configs;
}
@@ -1428,8 +1431,8 @@
for (auto data_interval_us : le_audio_output_data_interval_us_) {
PcmConfiguration pcm_config{
.sampleRateHz = sample_rate,
- .bitsPerSample = bits_per_sample,
.channelMode = channel_mode,
+ .bitsPerSample = bits_per_sample,
.dataIntervalUs = data_interval_us,
};
bool is_codec_config_valid =
@@ -1490,7 +1493,7 @@
std::vector<Lc3Configuration> GetBroadcastLc3SupportedList(bool supported) {
std::vector<Lc3Configuration> le_audio_codec_configs;
if (!supported) {
- Lc3Configuration lc3_config{.samplingFrequencyHz = 0, .pcmBitDepth = 0};
+ Lc3Configuration lc3_config{.pcmBitDepth = 0, .samplingFrequencyHz = 0};
le_audio_codec_configs.push_back(lc3_config);
return le_audio_codec_configs;
}
@@ -1650,8 +1653,8 @@
for (auto channel_mode : a2dp_channel_modes) {
PcmConfiguration pcm_config{
.sampleRateHz = sample_rate,
- .bitsPerSample = bits_per_sample,
.channelMode = channel_mode,
+ .bitsPerSample = bits_per_sample,
};
bool is_codec_config_valid = IsPcmConfigSupported(pcm_config);
DataMQDesc mq_desc;
diff --git a/broadcastradio/aidl/default/BroadcastRadio.cpp b/broadcastradio/aidl/default/BroadcastRadio.cpp
index 57c5133..0209a0e 100644
--- a/broadcastradio/aidl/default/BroadcastRadio.cpp
+++ b/broadcastradio/aidl/default/BroadcastRadio.cpp
@@ -22,11 +22,15 @@
#include <aidl/android/hardware/broadcastradio/Result.h>
#include <android-base/logging.h>
+#include <android-base/strings.h>
+
+#include <private/android_filesystem_config.h>
namespace aidl::android::hardware::broadcastradio {
using ::aidl::android::hardware::broadcastradio::utils::resultToInt;
using ::aidl::android::hardware::broadcastradio::utils::tunesTo;
+using ::android::base::EqualsIgnoreCase;
using ::ndk::ScopedAStatus;
using ::std::literals::chrono_literals::operator""ms;
using ::std::literals::chrono_literals::operator""s;
@@ -83,6 +87,15 @@
return info;
}
+static bool checkDumpCallerHasWritePermissions(int fd) {
+ uid_t uid = AIBinder_getCallingUid();
+ if (uid == AID_ROOT || uid == AID_SHELL || uid == AID_SYSTEM) {
+ return true;
+ }
+ dprintf(fd, "BroadcastRadio HAL dump must be root, shell or system\n");
+ return false;
+}
+
} // namespace
BroadcastRadio::BroadcastRadio(const VirtualRadio& virtualRadio)
@@ -470,4 +483,306 @@
"registering announcementListener is not supported");
}
+binder_status_t BroadcastRadio::dump(int fd, const char** args, uint32_t numArgs) {
+ if (numArgs == 0) {
+ return dumpsys(fd);
+ }
+
+ string option = string(args[0]);
+ if (EqualsIgnoreCase(option, "--help")) {
+ return cmdHelp(fd);
+ } else if (EqualsIgnoreCase(option, "--tune")) {
+ return cmdTune(fd, args, numArgs);
+ } else if (EqualsIgnoreCase(option, "--seek")) {
+ return cmdSeek(fd, args, numArgs);
+ } else if (EqualsIgnoreCase(option, "--step")) {
+ return cmdStep(fd, args, numArgs);
+ } else if (EqualsIgnoreCase(option, "--cancel")) {
+ return cmdCancel(fd, numArgs);
+ } else if (EqualsIgnoreCase(option, "--startProgramListUpdates")) {
+ return cmdStartProgramListUpdates(fd, args, numArgs);
+ } else if (EqualsIgnoreCase(option, "--stopProgramListUpdates")) {
+ return cmdStopProgramListUpdates(fd, numArgs);
+ }
+ dprintf(fd, "Invalid option: %s\n", option.c_str());
+ return STATUS_BAD_VALUE;
+}
+
+binder_status_t BroadcastRadio::dumpsys(int fd) {
+ if (!checkDumpCallerHasWritePermissions(fd)) {
+ return STATUS_PERMISSION_DENIED;
+ }
+ lock_guard<mutex> lk(mMutex);
+ dprintf(fd, "AmFmRegionConfig: %s\n", mAmFmConfig.toString().c_str());
+ dprintf(fd, "Properties: %s \n", mProperties.toString().c_str());
+ if (mIsTuneCompleted) {
+ dprintf(fd, "Tune completed\n");
+ } else {
+ dprintf(fd, "Tune not completed\n");
+ }
+ if (mCallback == nullptr) {
+ dprintf(fd, "No ITunerCallback registered\n");
+ } else {
+ dprintf(fd, "ITunerCallback registered\n");
+ }
+ dprintf(fd, "CurrentProgram: %s \n", mCurrentProgram.toString().c_str());
+ return STATUS_OK;
+}
+
+binder_status_t BroadcastRadio::cmdHelp(int fd) const {
+ dprintf(fd, "Usage: \n\n");
+ dprintf(fd, "[no args]: dumps focus listener / gain callback registered status\n");
+ dprintf(fd, "--help: shows this help\n");
+ dprintf(fd,
+ "--tune amfm <FREQUENCY>: tunes amfm radio to frequency (in Hz) specified: "
+ "frequency (int) \n"
+ "--tune dab <SID> <ENSEMBLE>: tunes dab radio to sid and ensemble specified: "
+ "sidExt (int), ensemble (int) \n");
+ dprintf(fd,
+ "--seek [up|down] <SKIP_SUB_CHANNEL>: seek with direction (up or down) and "
+ "option whether skipping sub channel: "
+ "skipSubChannel (string, should be either \"true\" or \"false\")\n");
+ dprintf(fd, "--step [up|down]: step in direction (up or down) specified\n");
+ dprintf(fd, "--cancel: cancel current pending tune, step, and seek\n");
+ dprintf(fd,
+ "--startProgramListUpdates <IDENTIFIER_TYPES> <IDENTIFIERS> <INCLUDE_CATEGORIES> "
+ "<EXCLUDE_MODIFICATIONS>: start update program list with the filter specified: "
+ "identifier types (string, in format <TYPE>,<TYPE>,...,<TYPE> or \"null\" (if empty), "
+ "where TYPE is int), "
+ "program identifiers (string, in format "
+ "<TYPE>:<VALUE>,<TYPE>:<VALUE>,...,<TYPE>:<VALUE> or \"null\" (if empty), "
+ "where TYPE is int and VALUE is long), "
+ "includeCategories (string, should be either \"true\" or \"false\"), "
+ "excludeModifications (string, should be either \"true\" or \"false\")\n");
+ dprintf(fd, "--stopProgramListUpdates: stop current pending program list updates\n");
+ dprintf(fd,
+ "Note on <TYPE> for --startProgramList command: it is int for identifier type. "
+ "Please see broadcastradio/aidl/android/hardware/broadcastradio/IdentifierType.aidl "
+ "for its definition.\n");
+ dprintf(fd,
+ "Note on <VALUE> for --startProgramList command: it is long type for identifier value. "
+ "Please see broadcastradio/aidl/android/hardware/broadcastradio/IdentifierType.aidl "
+ "for its value.\n");
+
+ return STATUS_OK;
+}
+
+binder_status_t BroadcastRadio::cmdTune(int fd, const char** args, uint32_t numArgs) {
+ if (!checkDumpCallerHasWritePermissions(fd)) {
+ return STATUS_PERMISSION_DENIED;
+ }
+ if (numArgs != 3 && numArgs != 4) {
+ dprintf(fd,
+ "Invalid number of arguments: please provide --tune amfm <FREQUENCY> "
+ "or --tune dab <SID> <ENSEMBLE>\n");
+ return STATUS_BAD_VALUE;
+ }
+ bool isDab = false;
+ if (EqualsIgnoreCase(string(args[1]), "dab")) {
+ isDab = true;
+ } else if (!EqualsIgnoreCase(string(args[1]), "amfm")) {
+ dprintf(fd, "Unknown radio type provided with tune: %s\n", args[1]);
+ return STATUS_BAD_VALUE;
+ }
+ ProgramSelector sel = {};
+ if (isDab) {
+ if (numArgs != 4) {
+ dprintf(fd,
+ "Invalid number of arguments: please provide --tune dab <SID> <ENSEMBLE>\n");
+ return STATUS_BAD_VALUE;
+ }
+ int sid;
+ if (!utils::parseArgInt(string(args[2]), &sid)) {
+ dprintf(fd, "Non-integer sid provided with tune: %s\n", string(args[2]).c_str());
+ return STATUS_BAD_VALUE;
+ }
+ int ensemble;
+ if (!utils::parseArgInt(string(args[3]), &ensemble)) {
+ dprintf(fd, "Non-integer ensemble provided with tune: %s\n", string(args[3]).c_str());
+ return STATUS_BAD_VALUE;
+ }
+ sel = utils::makeSelectorDab(sid, ensemble);
+ } else {
+ if (numArgs != 3) {
+ dprintf(fd, "Invalid number of arguments: please provide --tune amfm <FREQUENCY>\n");
+ return STATUS_BAD_VALUE;
+ }
+ int freq;
+ if (!utils::parseArgInt(string(args[2]), &freq)) {
+ dprintf(fd, "Non-integer frequency provided with tune: %s\n", string(args[2]).c_str());
+ return STATUS_BAD_VALUE;
+ }
+ sel = utils::makeSelectorAmfm(freq);
+ }
+
+ auto tuneResult = tune(sel);
+ if (!tuneResult.isOk()) {
+ dprintf(fd, "Unable to tune %s radio to %s\n", args[1], sel.toString().c_str());
+ return STATUS_BAD_VALUE;
+ }
+ dprintf(fd, "Tune %s radio to %s \n", args[1], sel.toString().c_str());
+ return STATUS_OK;
+}
+
+binder_status_t BroadcastRadio::cmdSeek(int fd, const char** args, uint32_t numArgs) {
+ if (!checkDumpCallerHasWritePermissions(fd)) {
+ return STATUS_PERMISSION_DENIED;
+ }
+ if (numArgs != 3) {
+ dprintf(fd,
+ "Invalid number of arguments: please provide --seek <DIRECTION> "
+ "<SKIP_SUB_CHANNEL>\n");
+ return STATUS_BAD_VALUE;
+ }
+ string seekDirectionIn = string(args[1]);
+ bool seekDirectionUp;
+ if (!utils::parseArgDirection(seekDirectionIn, &seekDirectionUp)) {
+ dprintf(fd, "Invalid direction (\"up\" or \"down\") provided with seek: %s\n",
+ seekDirectionIn.c_str());
+ return STATUS_BAD_VALUE;
+ }
+ string skipSubChannelIn = string(args[2]);
+ bool skipSubChannel;
+ if (!utils::parseArgBool(skipSubChannelIn, &skipSubChannel)) {
+ dprintf(fd, "Invalid skipSubChannel (\"true\" or \"false\") provided with seek: %s\n",
+ skipSubChannelIn.c_str());
+ return STATUS_BAD_VALUE;
+ }
+
+ auto seekResult = seek(seekDirectionUp, skipSubChannel);
+ if (!seekResult.isOk()) {
+ dprintf(fd, "Unable to seek in %s direction\n", seekDirectionIn.c_str());
+ return STATUS_BAD_VALUE;
+ }
+ dprintf(fd, "Seek in %s direction\n", seekDirectionIn.c_str());
+ return STATUS_OK;
+}
+
+binder_status_t BroadcastRadio::cmdStep(int fd, const char** args, uint32_t numArgs) {
+ if (!checkDumpCallerHasWritePermissions(fd)) {
+ return STATUS_PERMISSION_DENIED;
+ }
+ if (numArgs != 2) {
+ dprintf(fd, "Invalid number of arguments: please provide --step <DIRECTION>\n");
+ return STATUS_BAD_VALUE;
+ }
+ string stepDirectionIn = string(args[1]);
+ bool stepDirectionUp;
+ if (!utils::parseArgDirection(stepDirectionIn, &stepDirectionUp)) {
+ dprintf(fd, "Invalid direction (\"up\" or \"down\") provided with step: %s\n",
+ stepDirectionIn.c_str());
+ return STATUS_BAD_VALUE;
+ }
+
+ auto stepResult = step(stepDirectionUp);
+ if (!stepResult.isOk()) {
+ dprintf(fd, "Unable to step in %s direction\n", stepDirectionIn.c_str());
+ return STATUS_BAD_VALUE;
+ }
+ dprintf(fd, "Step in %s direction\n", stepDirectionIn.c_str());
+ return STATUS_OK;
+}
+
+binder_status_t BroadcastRadio::cmdCancel(int fd, uint32_t numArgs) {
+ if (!checkDumpCallerHasWritePermissions(fd)) {
+ return STATUS_PERMISSION_DENIED;
+ }
+ if (numArgs != 1) {
+ dprintf(fd,
+ "Invalid number of arguments: please provide --cancel "
+ "only and no more arguments\n");
+ return STATUS_BAD_VALUE;
+ }
+
+ auto cancelResult = cancel();
+ if (!cancelResult.isOk()) {
+ dprintf(fd, "Unable to cancel pending tune, seek, and step\n");
+ return STATUS_BAD_VALUE;
+ }
+ dprintf(fd, "Canceled pending tune, seek, and step\n");
+ return STATUS_OK;
+}
+
+binder_status_t BroadcastRadio::cmdStartProgramListUpdates(int fd, const char** args,
+ uint32_t numArgs) {
+ if (!checkDumpCallerHasWritePermissions(fd)) {
+ return STATUS_PERMISSION_DENIED;
+ }
+ if (numArgs != 5) {
+ dprintf(fd,
+ "Invalid number of arguments: please provide --startProgramListUpdates "
+ "<IDENTIFIER_TYPES> <IDENTIFIERS> <INCLUDE_CATEGORIES> "
+ "<EXCLUDE_MODIFICATIONS>\n");
+ return STATUS_BAD_VALUE;
+ }
+ string filterTypesStr = string(args[1]);
+ std::vector<IdentifierType> filterTypeList;
+ if (!EqualsIgnoreCase(filterTypesStr, "null") &&
+ !utils::parseArgIdentifierTypeArray(filterTypesStr, &filterTypeList)) {
+ dprintf(fd,
+ "Invalid identifier types provided with startProgramListUpdates: %s, "
+ "should be: <TYPE>,<TYPE>,...,<TYPE>\n",
+ filterTypesStr.c_str());
+ return STATUS_BAD_VALUE;
+ }
+ string filtersStr = string(args[2]);
+ std::vector<ProgramIdentifier> filterList;
+ if (!EqualsIgnoreCase(filtersStr, "null") &&
+ !utils::parseProgramIdentifierList(filtersStr, &filterList)) {
+ dprintf(fd,
+ "Invalid program identifiers provided with startProgramListUpdates: %s, "
+ "should be: <TYPE>:<VALUE>,<TYPE>:<VALUE>,...,<TYPE>:<VALUE>\n",
+ filtersStr.c_str());
+ return STATUS_BAD_VALUE;
+ }
+ string includeCategoriesStr = string(args[3]);
+ bool includeCategories;
+ if (!utils::parseArgBool(includeCategoriesStr, &includeCategories)) {
+ dprintf(fd,
+ "Invalid includeCategories (\"true\" or \"false\") "
+ "provided with startProgramListUpdates : %s\n",
+ includeCategoriesStr.c_str());
+ return STATUS_BAD_VALUE;
+ }
+ string excludeModificationsStr = string(args[4]);
+ bool excludeModifications;
+ if (!utils::parseArgBool(excludeModificationsStr, &excludeModifications)) {
+ dprintf(fd,
+ "Invalid excludeModifications(\"true\" or \"false\") "
+ "provided with startProgramListUpdates : %s\n",
+ excludeModificationsStr.c_str());
+ return STATUS_BAD_VALUE;
+ }
+ ProgramFilter filter = {filterTypeList, filterList, includeCategories, excludeModifications};
+
+ auto updateResult = startProgramListUpdates(filter);
+ if (!updateResult.isOk()) {
+ dprintf(fd, "Unable to start program list update for filter %s \n",
+ filter.toString().c_str());
+ return STATUS_BAD_VALUE;
+ }
+ dprintf(fd, "Start program list update for filter %s\n", filter.toString().c_str());
+ return STATUS_OK;
+}
+
+binder_status_t BroadcastRadio::cmdStopProgramListUpdates(int fd, uint32_t numArgs) {
+ if (!checkDumpCallerHasWritePermissions(fd)) {
+ return STATUS_PERMISSION_DENIED;
+ }
+ if (numArgs != 1) {
+ dprintf(fd,
+ "Invalid number of arguments: please provide --stopProgramListUpdates "
+ "only and no more arguments\n");
+ return STATUS_BAD_VALUE;
+ }
+
+ auto stopResult = stopProgramListUpdates();
+ if (!stopResult.isOk()) {
+ dprintf(fd, "Unable to stop pending program list update\n");
+ return STATUS_BAD_VALUE;
+ }
+ dprintf(fd, "Stop pending program list update\n");
+ return STATUS_OK;
+}
+
} // namespace aidl::android::hardware::broadcastradio
diff --git a/broadcastradio/aidl/default/BroadcastRadio.h b/broadcastradio/aidl/default/BroadcastRadio.h
index 52d394e..1c85ddc 100644
--- a/broadcastradio/aidl/default/BroadcastRadio.h
+++ b/broadcastradio/aidl/default/BroadcastRadio.h
@@ -62,6 +62,7 @@
const std::shared_ptr<IAnnouncementListener>& listener,
const std::vector<AnnouncementType>& enabled,
std::shared_ptr<ICloseHandle>* returnCloseHandle) override;
+ binder_status_t dump(int fd, const char** args, uint32_t numArgs) override;
private:
const VirtualRadio& mVirtualRadio;
@@ -77,6 +78,16 @@
std::optional<AmFmBandRange> getAmFmRangeLocked() const;
void cancelLocked();
ProgramInfo tuneInternalLocked(const ProgramSelector& sel);
+
+ binder_status_t cmdHelp(int fd) const;
+ binder_status_t cmdTune(int fd, const char** args, uint32_t numArgs);
+ binder_status_t cmdSeek(int fd, const char** args, uint32_t numArgs);
+ binder_status_t cmdStep(int fd, const char** args, uint32_t numArgs);
+ binder_status_t cmdCancel(int fd, uint32_t numArgs);
+ binder_status_t cmdStartProgramListUpdates(int fd, const char** args, uint32_t numArgs);
+ binder_status_t cmdStopProgramListUpdates(int fd, uint32_t numArgs);
+
+ binder_status_t dumpsys(int fd);
};
} // namespace aidl::android::hardware::broadcastradio
diff --git a/broadcastradio/aidl/vts/Android.bp b/broadcastradio/aidl/vts/Android.bp
new file mode 100644
index 0000000..b60387e
--- /dev/null
+++ b/broadcastradio/aidl/vts/Android.bp
@@ -0,0 +1,47 @@
+// Copyright (C) 2022 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.
+
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "hardware_interfaces_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
+cc_test {
+ name: "VtsHalBroadcastradioAidlTargetTest",
+ defaults: [
+ "VtsHalTargetTestDefaults",
+ "use_libaidlvintf_gtest_helper_static",
+ ],
+ tidy_timeout_srcs: ["src/*.cpp"],
+ srcs: ["src/*.cpp"],
+ shared_libs: [
+ "libbinder_ndk",
+ "libbase",
+ "libxml2",
+ ],
+ static_libs: [
+ "android.hardware.broadcastradio-V1-ndk",
+ "android.hardware.broadcastradio@common-utils-aidl-lib",
+ "android.hardware.broadcastradio@vts-utils-lib",
+ "libgmock",
+ ],
+ test_suites: [
+ "general-tests",
+ "vts",
+ ],
+}
diff --git a/broadcastradio/aidl/vts/OWNERS b/broadcastradio/aidl/vts/OWNERS
new file mode 100644
index 0000000..302fdd7
--- /dev/null
+++ b/broadcastradio/aidl/vts/OWNERS
@@ -0,0 +1,4 @@
+xuweilin@google.com
+oscarazu@google.com
+ericjeong@google.com
+keunyoung@google.com
diff --git a/broadcastradio/aidl/vts/src/VtsHalBroadcastradioAidlTargetTest.cpp b/broadcastradio/aidl/vts/src/VtsHalBroadcastradioAidlTargetTest.cpp
new file mode 100644
index 0000000..5a56846
--- /dev/null
+++ b/broadcastradio/aidl/vts/src/VtsHalBroadcastradioAidlTargetTest.cpp
@@ -0,0 +1,1103 @@
+/*
+ * Copyright (C) 2022 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 EGMOCK_VERBOSE 1
+
+#include <aidl/android/hardware/broadcastradio/BnAnnouncementListener.h>
+#include <aidl/android/hardware/broadcastradio/BnTunerCallback.h>
+#include <aidl/android/hardware/broadcastradio/ConfigFlag.h>
+#include <aidl/android/hardware/broadcastradio/IBroadcastRadio.h>
+#include <aidl/android/hardware/broadcastradio/ProgramListChunk.h>
+#include <aidl/android/hardware/broadcastradio/ProgramSelector.h>
+#include <aidl/android/hardware/broadcastradio/VendorKeyValue.h>
+#include <android-base/logging.h>
+#include <android-base/strings.h>
+#include <android-base/thread_annotations.h>
+#include <android/binder_manager.h>
+#include <android/binder_process.h>
+
+#include <aidl/Gtest.h>
+#include <aidl/Vintf.h>
+#include <broadcastradio-utils-aidl/Utils.h>
+#include <broadcastradio-vts-utils/mock-timeout.h>
+#include <cutils/bitops.h>
+#include <gmock/gmock.h>
+
+#include <chrono>
+#include <optional>
+#include <regex>
+
+namespace aidl::android::hardware::broadcastradio::vts {
+
+namespace {
+
+using ::aidl::android::hardware::broadcastradio::utils::makeIdentifier;
+using ::aidl::android::hardware::broadcastradio::utils::makeSelectorAmfm;
+using ::aidl::android::hardware::broadcastradio::utils::resultToInt;
+using ::ndk::ScopedAStatus;
+using ::ndk::SharedRefBase;
+using ::std::string;
+using ::std::vector;
+using ::testing::_;
+using ::testing::AnyNumber;
+using ::testing::ByMove;
+using ::testing::DoAll;
+using ::testing::Invoke;
+using ::testing::SaveArg;
+
+namespace bcutils = ::aidl::android::hardware::broadcastradio::utils;
+
+inline constexpr std::chrono::seconds kTuneTimeoutSec =
+ std::chrono::seconds(IBroadcastRadio::TUNER_TIMEOUT_MS * 1000);
+inline constexpr std::chrono::seconds kProgramListScanTimeoutSec =
+ std::chrono::seconds(IBroadcastRadio::LIST_COMPLETE_TIMEOUT_MS * 1000);
+
+const ConfigFlag kConfigFlagValues[] = {
+ ConfigFlag::FORCE_MONO,
+ ConfigFlag::FORCE_ANALOG,
+ ConfigFlag::FORCE_DIGITAL,
+ ConfigFlag::RDS_AF,
+ ConfigFlag::RDS_REG,
+ ConfigFlag::DAB_DAB_LINKING,
+ ConfigFlag::DAB_FM_LINKING,
+ ConfigFlag::DAB_DAB_SOFT_LINKING,
+ ConfigFlag::DAB_FM_SOFT_LINKING,
+};
+
+void printSkipped(const string& msg) {
+ const auto testInfo = testing::UnitTest::GetInstance()->current_test_info();
+ LOG(INFO) << "[ SKIPPED ] " << testInfo->test_case_name() << "." << testInfo->name()
+ << " with message: " << msg;
+}
+
+bool isValidAmFmFreq(int64_t freq) {
+ ProgramIdentifier id = bcutils::makeIdentifier(IdentifierType::AMFM_FREQUENCY_KHZ, freq);
+ return bcutils::isValid(id);
+}
+
+void validateRange(const AmFmBandRange& range) {
+ EXPECT_TRUE(isValidAmFmFreq(range.lowerBound));
+ EXPECT_TRUE(isValidAmFmFreq(range.upperBound));
+ EXPECT_LT(range.lowerBound, range.upperBound);
+ EXPECT_GT(range.spacing, 0u);
+ EXPECT_EQ((range.upperBound - range.lowerBound) % range.spacing, 0u);
+}
+
+bool supportsFM(const AmFmRegionConfig& config) {
+ for (const auto& range : config.ranges) {
+ if (bcutils::getBand(range.lowerBound) == bcutils::FrequencyBand::FM) {
+ return true;
+ }
+ }
+ return false;
+}
+
+} // namespace
+
+class TunerCallbackMock : public BnTunerCallback {
+ public:
+ TunerCallbackMock();
+
+ MOCK_METHOD2(onTuneFailed, ScopedAStatus(Result, const ProgramSelector&));
+ MOCK_TIMEOUT_METHOD1(onCurrentProgramInfoChangedMock, ScopedAStatus(const ProgramInfo&));
+ ScopedAStatus onCurrentProgramInfoChanged(const ProgramInfo& info) override;
+ ScopedAStatus onProgramListUpdated(const ProgramListChunk& chunk) override;
+ MOCK_METHOD1(onAntennaStateChange, ScopedAStatus(bool connected));
+ MOCK_METHOD1(onParametersUpdated, ScopedAStatus(const vector<VendorKeyValue>& parameters));
+ MOCK_METHOD2(onConfigFlagUpdated, ScopedAStatus(ConfigFlag in_flag, bool in_value));
+ MOCK_TIMEOUT_METHOD0(onProgramListReady, void());
+
+ std::mutex mLock;
+ bcutils::ProgramInfoSet mProgramList GUARDED_BY(mLock);
+};
+
+struct AnnouncementListenerMock : public BnAnnouncementListener {
+ MOCK_METHOD1(onListUpdated, ScopedAStatus(const vector<Announcement>&));
+};
+
+class BroadcastRadioHalTest : public testing::TestWithParam<string> {
+ protected:
+ void SetUp() override;
+ void TearDown() override;
+
+ bool getAmFmRegionConfig(bool full, AmFmRegionConfig* config);
+ std::optional<bcutils::ProgramInfoSet> getProgramList();
+ std::optional<bcutils::ProgramInfoSet> getProgramList(const ProgramFilter& filter);
+
+ std::shared_ptr<IBroadcastRadio> mModule;
+ Properties mProperties;
+ std::shared_ptr<TunerCallbackMock> mCallback = SharedRefBase::make<TunerCallbackMock>();
+};
+
+MATCHER_P(InfoHasId, id, string(negation ? "does not contain" : "contains") + " " + id.toString()) {
+ vector<int> ids = bcutils::getAllIds(arg.selector, id.type);
+ return ids.end() != find(ids.begin(), ids.end(), id.value);
+}
+
+TunerCallbackMock::TunerCallbackMock() {
+ EXPECT_TIMEOUT_CALL(*this, onCurrentProgramInfoChangedMock, _).Times(AnyNumber());
+
+ // we expect the antenna is connected through the whole test
+ EXPECT_CALL(*this, onAntennaStateChange(false)).Times(0);
+}
+
+ScopedAStatus TunerCallbackMock::onCurrentProgramInfoChanged(const ProgramInfo& info) {
+ for (const auto& id : info.selector) {
+ EXPECT_NE(id.type, IdentifierType::INVALID);
+ }
+
+ IdentifierType logically = info.logicallyTunedTo.type;
+ // This field is required for currently tuned program and should be INVALID
+ // for entries from the program list.
+ EXPECT_TRUE(logically == IdentifierType::AMFM_FREQUENCY_KHZ ||
+ logically == IdentifierType::RDS_PI ||
+ logically == IdentifierType::HD_STATION_ID_EXT ||
+ logically == IdentifierType::DAB_SID_EXT ||
+ logically == IdentifierType::DRMO_SERVICE_ID ||
+ logically == IdentifierType::SXM_SERVICE_ID ||
+ (logically >= IdentifierType::VENDOR_START &&
+ logically <= IdentifierType::VENDOR_END) ||
+ logically > IdentifierType::SXM_CHANNEL);
+
+ IdentifierType physically = info.physicallyTunedTo.type;
+ // ditto (see "logically" above)
+ EXPECT_TRUE(physically == IdentifierType::AMFM_FREQUENCY_KHZ ||
+ physically == IdentifierType::DAB_ENSEMBLE ||
+ physically == IdentifierType::DRMO_FREQUENCY_KHZ ||
+ physically == IdentifierType::SXM_CHANNEL ||
+ (physically >= IdentifierType::VENDOR_START &&
+ physically <= IdentifierType::VENDOR_END) ||
+ physically > IdentifierType::SXM_CHANNEL);
+
+ if (logically == IdentifierType::AMFM_FREQUENCY_KHZ) {
+ std::optional<string> ps = bcutils::getMetadataString(info, Metadata::rdsPs);
+ if (ps.has_value()) {
+ EXPECT_NE(::android::base::Trim(*ps), "")
+ << "Don't use empty RDS_PS as an indicator of missing RSD PS data.";
+ }
+ }
+
+ return onCurrentProgramInfoChangedMock(info);
+}
+
+ScopedAStatus TunerCallbackMock::onProgramListUpdated(const ProgramListChunk& chunk) {
+ std::lock_guard<std::mutex> lk(mLock);
+
+ updateProgramList(chunk, &mProgramList);
+
+ if (chunk.complete) {
+ onProgramListReady();
+ }
+
+ return ndk::ScopedAStatus::ok();
+}
+
+void BroadcastRadioHalTest::SetUp() {
+ EXPECT_EQ(mModule.get(), nullptr) << "Module is already open";
+
+ // lookup AIDL service (radio module)
+ AIBinder* binder = AServiceManager_waitForService(GetParam().c_str());
+ ASSERT_NE(binder, nullptr);
+ mModule = IBroadcastRadio::fromBinder(ndk::SpAIBinder(binder));
+ ASSERT_NE(mModule, nullptr) << "Couldn't find broadcast radio HAL implementation";
+
+ // get module properties
+ auto propResult = mModule->getProperties(&mProperties);
+
+ ASSERT_TRUE(propResult.isOk());
+ EXPECT_FALSE(mProperties.maker.empty());
+ EXPECT_FALSE(mProperties.product.empty());
+ EXPECT_GT(mProperties.supportedIdentifierTypes.size(), 0u);
+
+ // set callback
+ EXPECT_TRUE(mModule->setTunerCallback(mCallback).isOk());
+}
+
+void BroadcastRadioHalTest::TearDown() {
+ if (mModule) {
+ ASSERT_TRUE(mModule->unsetTunerCallback().isOk());
+ }
+}
+
+bool BroadcastRadioHalTest::getAmFmRegionConfig(bool full, AmFmRegionConfig* config) {
+ auto halResult = mModule->getAmFmRegionConfig(full, config);
+
+ if (halResult.getServiceSpecificError() == resultToInt(Result::NOT_SUPPORTED)) {
+ return false;
+ }
+
+ EXPECT_TRUE(halResult.isOk());
+ return halResult.isOk();
+}
+
+std::optional<bcutils::ProgramInfoSet> BroadcastRadioHalTest::getProgramList() {
+ ProgramFilter emptyFilter = {};
+ return getProgramList(emptyFilter);
+}
+
+std::optional<bcutils::ProgramInfoSet> BroadcastRadioHalTest::getProgramList(
+ const ProgramFilter& filter) {
+ EXPECT_TIMEOUT_CALL(*mCallback, onProgramListReady).Times(AnyNumber());
+
+ auto startResult = mModule->startProgramListUpdates(filter);
+
+ if (startResult.getServiceSpecificError() == resultToInt(Result::NOT_SUPPORTED)) {
+ printSkipped("Program list not supported");
+ return std::nullopt;
+ }
+ EXPECT_TRUE(startResult.isOk());
+ if (!startResult.isOk()) {
+ return std::nullopt;
+ }
+ EXPECT_TIMEOUT_CALL_WAIT(*mCallback, onProgramListReady, kProgramListScanTimeoutSec);
+
+ auto stopResult = mModule->stopProgramListUpdates();
+
+ EXPECT_TRUE(stopResult.isOk());
+
+ return mCallback->mProgramList;
+}
+
+/**
+ * Test setting tuner callback to null.
+ *
+ * Verifies that:
+ * - Setting to a null tuner callback results with INVALID_ARGUMENTS.
+ */
+TEST_P(BroadcastRadioHalTest, TunerCallbackFailsWithNull) {
+ LOG(DEBUG) << "TunerCallbackFailsWithNull Test";
+
+ auto halResult = mModule->setTunerCallback(nullptr);
+
+ EXPECT_EQ(halResult.getServiceSpecificError(), resultToInt(Result::INVALID_ARGUMENTS));
+}
+
+/**
+ * Test fetching AM/FM regional configuration.
+ *
+ * Verifies that:
+ * - AM/FM regional configuration is either set at startup or not supported at all by the hardware;
+ * - FM Deemphasis and RDS are correctly configured for FM-capable radio;
+ */
+TEST_P(BroadcastRadioHalTest, GetAmFmRegionConfig) {
+ LOG(DEBUG) << "GetAmFmRegionConfig Test";
+
+ AmFmRegionConfig config;
+
+ bool supported = getAmFmRegionConfig(/* full= */ false, &config);
+
+ if (!supported) {
+ printSkipped("AM/FM not supported");
+ return;
+ }
+
+ EXPECT_LE(popcountll(static_cast<unsigned long long>(config.fmDeemphasis)), 1);
+ EXPECT_LE(popcountll(static_cast<unsigned long long>(config.fmRds)), 1);
+
+ if (supportsFM(config)) {
+ EXPECT_EQ(popcountll(static_cast<unsigned long long>(config.fmDeemphasis)), 1);
+ }
+}
+
+/**
+ * Test fetching ranges of AM/FM regional configuration.
+ *
+ * Verifies that:
+ * - AM/FM regional configuration is either set at startup or not supported at all by the hardware;
+ * - there is at least one AM/FM band configured;
+ * - all channel grids (frequency ranges and spacings) are valid;
+ * - seek spacing is a multiple of the manual spacing value.
+ */
+TEST_P(BroadcastRadioHalTest, GetAmFmRegionConfigRanges) {
+ LOG(DEBUG) << "GetAmFmRegionConfigRanges Test";
+
+ AmFmRegionConfig config;
+
+ bool supported = getAmFmRegionConfig(/* full= */ false, &config);
+
+ if (!supported) {
+ printSkipped("AM/FM not supported");
+ return;
+ }
+
+ EXPECT_GT(config.ranges.size(), 0u);
+ for (const auto& range : config.ranges) {
+ validateRange(range);
+ EXPECT_EQ(range.seekSpacing % range.spacing, 0u);
+ EXPECT_GE(range.seekSpacing, range.spacing);
+ }
+}
+
+/**
+ * Test fetching FM regional capabilities.
+ *
+ * Verifies that:
+ * - AM/FM regional capabilities are either available or not supported at all by the hardware;
+ * - there is at least one de-emphasis filter mode supported for FM-capable radio;
+ */
+TEST_P(BroadcastRadioHalTest, GetAmFmRegionConfigCapabilitiesForFM) {
+ LOG(DEBUG) << "GetAmFmRegionConfigCapabilitiesForFM Test";
+
+ AmFmRegionConfig config;
+
+ bool supported = getAmFmRegionConfig(/* full= */ true, &config);
+
+ if (supported && supportsFM(config)) {
+ EXPECT_GE(popcountll(static_cast<unsigned long long>(config.fmDeemphasis)), 1);
+ } else {
+ printSkipped("FM not supported");
+ }
+}
+
+/**
+ * Test fetching the ranges of AM/FM regional capabilities.
+ *
+ * Verifies that:
+ * - AM/FM regional capabilities are either available or not supported at all by the hardware;
+ * - there is at least one AM/FM range supported;
+ * - all channel grids (frequency ranges and spacings) are valid;
+ * - seek spacing is not set.
+ */
+TEST_P(BroadcastRadioHalTest, GetAmFmRegionConfigCapabilitiesRanges) {
+ LOG(DEBUG) << "GetAmFmRegionConfigCapabilitiesRanges Test";
+
+ AmFmRegionConfig config;
+
+ bool supported = getAmFmRegionConfig(/* full= */ true, &config);
+
+ if (!supported) {
+ printSkipped("AM/FM not supported");
+ return;
+ }
+
+ EXPECT_GT(config.ranges.size(), 0u);
+
+ for (const auto& range : config.ranges) {
+ validateRange(range);
+ EXPECT_EQ(range.seekSpacing, 0u);
+ }
+}
+
+/**
+ * Test fetching DAB regional configuration.
+ *
+ * Verifies that:
+ * - DAB regional configuration is either set at startup or not supported at all by the hardware;
+ * - all channel labels match correct format;
+ * - all channel frequencies are in correct range.
+ */
+TEST_P(BroadcastRadioHalTest, GetDabRegionConfig) {
+ LOG(DEBUG) << "GetDabRegionConfig Test";
+ vector<DabTableEntry> config;
+
+ auto halResult = mModule->getDabRegionConfig(&config);
+
+ if (halResult.getServiceSpecificError() == resultToInt(Result::NOT_SUPPORTED)) {
+ printSkipped("DAB not supported");
+ return;
+ }
+ ASSERT_TRUE(halResult.isOk());
+
+ std::regex re("^[A-Z0-9][A-Z0-9 ]{0,5}[A-Z0-9]$");
+
+ for (const auto& entry : config) {
+ EXPECT_TRUE(std::regex_match(string(entry.label), re));
+
+ ProgramIdentifier id =
+ bcutils::makeIdentifier(IdentifierType::DAB_FREQUENCY_KHZ, entry.frequencyKhz);
+ EXPECT_TRUE(bcutils::isValid(id));
+ }
+}
+
+/**
+ * Test tuning without tuner callback set.
+ *
+ * Verifies that:
+ * - No tuner callback set results in INVALID_STATE, regardless of whether the selector is
+ * supported.
+ */
+TEST_P(BroadcastRadioHalTest, TuneFailsWithoutTunerCallback) {
+ LOG(DEBUG) << "TuneFailsWithoutTunerCallback Test";
+
+ mModule->unsetTunerCallback();
+ int64_t freq = 90900; // 90.9 FM
+ ProgramSelector sel = makeSelectorAmfm(freq);
+
+ auto result = mModule->tune(sel);
+
+ EXPECT_EQ(result.getServiceSpecificError(), resultToInt(Result::INVALID_STATE));
+}
+
+/**
+ * Test tuning with selectors that can be not supported.
+ *
+ * Verifies that:
+ * - if the selector is not supported, an invalid value results with NOT_SUPPORTED, regardless of
+ * whether it is valid;
+ * - if it is supported, the test is ignored;
+ */
+TEST_P(BroadcastRadioHalTest, TuneFailsWithNotSupported) {
+ LOG(DEBUG) << "TuneFailsWithInvalid Test";
+
+ vector<ProgramIdentifier> supportTestId = {
+ makeIdentifier(IdentifierType::AMFM_FREQUENCY_KHZ, 0), // invalid
+ makeIdentifier(IdentifierType::AMFM_FREQUENCY_KHZ, 94900), // valid
+ makeIdentifier(IdentifierType::RDS_PI, 0x10000), // invalid
+ makeIdentifier(IdentifierType::RDS_PI, 0x1001), // valid
+ makeIdentifier(IdentifierType::HD_STATION_ID_EXT, 0x100000000), // invalid
+ makeIdentifier(IdentifierType::HD_STATION_ID_EXT, 0x10000001), // valid
+ makeIdentifier(IdentifierType::DAB_SID_EXT, 0), // invalid
+ makeIdentifier(IdentifierType::DAB_SID_EXT, 0xA00001), // valid
+ makeIdentifier(IdentifierType::DRMO_SERVICE_ID, 0x100000000), // invalid
+ makeIdentifier(IdentifierType::DRMO_SERVICE_ID, 0x10000001), // valid
+ makeIdentifier(IdentifierType::SXM_SERVICE_ID, 0x100000000), // invalid
+ makeIdentifier(IdentifierType::SXM_SERVICE_ID, 0x10000001), // valid
+ };
+
+ auto notSupportedError = resultToInt(Result::NOT_SUPPORTED);
+ for (const auto& id : supportTestId) {
+ ProgramSelector sel{id, {}};
+
+ auto result = mModule->tune(sel);
+
+ if (!bcutils::isSupported(mProperties, sel)) {
+ EXPECT_EQ(result.getServiceSpecificError(), notSupportedError);
+ }
+ }
+}
+
+/**
+ * Test tuning with invalid selectors.
+ *
+ * Verifies that:
+ * - if the selector is not supported, it's ignored;
+ * - if it is supported, an invalid value results with INVALID_ARGUMENTS;
+ */
+TEST_P(BroadcastRadioHalTest, TuneFailsWithInvalid) {
+ LOG(DEBUG) << "TuneFailsWithInvalid Test";
+
+ vector<ProgramIdentifier> invalidId = {
+ makeIdentifier(IdentifierType::AMFM_FREQUENCY_KHZ, 0),
+ makeIdentifier(IdentifierType::RDS_PI, 0x10000),
+ makeIdentifier(IdentifierType::HD_STATION_ID_EXT, 0x100000000),
+ makeIdentifier(IdentifierType::DAB_SID_EXT, 0),
+ makeIdentifier(IdentifierType::DRMO_SERVICE_ID, 0x100000000),
+ makeIdentifier(IdentifierType::SXM_SERVICE_ID, 0x100000000),
+ };
+
+ auto invalidArgumentsError = resultToInt(Result::INVALID_ARGUMENTS);
+ for (const auto& id : invalidId) {
+ ProgramSelector sel{id, {}};
+
+ auto result = mModule->tune(sel);
+
+ if (bcutils::isSupported(mProperties, sel)) {
+ EXPECT_EQ(result.getServiceSpecificError(), invalidArgumentsError);
+ }
+ }
+}
+
+/**
+ * Test tuning with empty program selector.
+ *
+ * Verifies that:
+ * - tune fails with NOT_SUPPORTED when program selector is not initialized.
+ */
+TEST_P(BroadcastRadioHalTest, TuneFailsWithEmpty) {
+ LOG(DEBUG) << "TuneFailsWithEmpty Test";
+
+ // Program type is 1-based, so 0 will always be invalid.
+ ProgramSelector sel = {};
+
+ auto result = mModule->tune(sel);
+
+ ASSERT_EQ(result.getServiceSpecificError(), resultToInt(Result::NOT_SUPPORTED));
+}
+
+/**
+ * Test tuning with FM selector.
+ *
+ * Verifies that:
+ * - if AM/FM selector is not supported, the method returns NOT_SUPPORTED;
+ * - if it is supported, the method succeeds;
+ * - after a successful tune call, onCurrentProgramInfoChanged callback is
+ * invoked carrying a proper selector;
+ * - program changes exactly to what was requested.
+ */
+TEST_P(BroadcastRadioHalTest, FmTune) {
+ LOG(DEBUG) << "FmTune Test";
+
+ int64_t freq = 90900; // 90.9 FM
+ ProgramSelector sel = makeSelectorAmfm(freq);
+ // try tuning
+ ProgramInfo infoCb = {};
+ EXPECT_TIMEOUT_CALL(*mCallback, onCurrentProgramInfoChangedMock,
+ InfoHasId(makeIdentifier(IdentifierType::AMFM_FREQUENCY_KHZ, freq)))
+ .Times(AnyNumber())
+ .WillOnce(DoAll(SaveArg<0>(&infoCb), testing::Return(ByMove(ndk::ScopedAStatus::ok()))))
+ .WillRepeatedly(testing::InvokeWithoutArgs([] { return ndk::ScopedAStatus::ok(); }));
+
+ auto result = mModule->tune(sel);
+
+ // expect a failure if it's not supported
+ if (!bcutils::isSupported(mProperties, sel)) {
+ EXPECT_EQ(result.getServiceSpecificError(), resultToInt(Result::NOT_SUPPORTED));
+ return;
+ }
+
+ // expect a callback if it succeeds
+ EXPECT_TRUE(result.isOk());
+ EXPECT_TIMEOUT_CALL_WAIT(*mCallback, onCurrentProgramInfoChangedMock, kTuneTimeoutSec);
+
+ LOG(DEBUG) << "Current program info: " << infoCb.toString();
+
+ // it should tune exactly to what was requested
+ vector<int> freqs = bcutils::getAllIds(infoCb.selector, IdentifierType::AMFM_FREQUENCY_KHZ);
+ EXPECT_NE(freqs.end(), find(freqs.begin(), freqs.end(), freq))
+ << "FM freq " << freq << " kHz is not sent back by callback.";
+}
+
+/**
+ * Test tuning with DAB selector.
+ *
+ * Verifies that:
+ * - if DAB selector is not supported, the method returns NOT_SUPPORTED;
+ * - if it is supported, the method succeeds;
+ * - after a successful tune call, onCurrentProgramInfoChanged callback is
+ * invoked carrying a proper selector;
+ * - program changes exactly to what was requested.
+ */
+TEST_P(BroadcastRadioHalTest, DabTune) {
+ LOG(DEBUG) << "DabTune Test";
+ vector<DabTableEntry> config;
+
+ auto halResult = mModule->getDabRegionConfig(&config);
+
+ if (halResult.getServiceSpecificError() == resultToInt(Result::NOT_SUPPORTED)) {
+ printSkipped("DAB not supported");
+ return;
+ }
+ ASSERT_TRUE(halResult.isOk());
+ ASSERT_NE(config.size(), 0U);
+
+ // TODO(245787803): use a DAB frequency that can actually be tuned to.
+ ProgramSelector sel = {};
+ int64_t freq = config[config.size() / 2].frequencyKhz;
+ sel.primaryId = makeIdentifier(IdentifierType::DAB_FREQUENCY_KHZ, freq);
+
+ // try tuning
+ ProgramInfo infoCb = {};
+ EXPECT_TIMEOUT_CALL(*mCallback, onCurrentProgramInfoChangedMock,
+ InfoHasId(makeIdentifier(IdentifierType::DAB_FREQUENCY_KHZ, freq)))
+ .Times(AnyNumber())
+ .WillOnce(
+ DoAll(SaveArg<0>(&infoCb), testing::Return(ByMove(ndk::ScopedAStatus::ok()))));
+
+ auto result = mModule->tune(sel);
+
+ // expect a failure if it's not supported
+ if (!bcutils::isSupported(mProperties, sel)) {
+ EXPECT_EQ(result.getServiceSpecificError(), resultToInt(Result::NOT_SUPPORTED));
+ return;
+ }
+
+ // expect a callback if it succeeds
+ EXPECT_TRUE(result.isOk());
+ EXPECT_TIMEOUT_CALL_WAIT(*mCallback, onCurrentProgramInfoChangedMock, kTuneTimeoutSec);
+ LOG(DEBUG) << "Current program info: " << infoCb.toString();
+
+ // it should tune exactly to what was requested
+ vector<int> freqs = bcutils::getAllIds(infoCb.selector, IdentifierType::DAB_FREQUENCY_KHZ);
+ EXPECT_NE(freqs.end(), find(freqs.begin(), freqs.end(), freq))
+ << "DAB freq " << freq << " kHz is not sent back by callback.";
+ ;
+}
+
+/**
+ * Test seeking to next/prev station via IBroadcastRadio::seek().
+ *
+ * Verifies that:
+ * - the method succeeds;
+ * - the program info is changed within kTuneTimeoutSec;
+ * - works both directions and with or without skipping sub-channel.
+ */
+TEST_P(BroadcastRadioHalTest, Seek) {
+ LOG(DEBUG) << "Seek Test";
+
+ EXPECT_TIMEOUT_CALL(*mCallback, onCurrentProgramInfoChangedMock, _).Times(AnyNumber());
+
+ auto result = mModule->seek(/* in_directionUp= */ true, /* in_skipSubChannel= */ true);
+
+ if (result.getServiceSpecificError() == resultToInt(Result::NOT_SUPPORTED)) {
+ printSkipped("Seek not supported");
+ return;
+ }
+
+ EXPECT_TRUE(result.isOk());
+ EXPECT_TIMEOUT_CALL_WAIT(*mCallback, onCurrentProgramInfoChangedMock, kTuneTimeoutSec);
+
+ EXPECT_TIMEOUT_CALL(*mCallback, onCurrentProgramInfoChangedMock, _).Times(AnyNumber());
+
+ result = mModule->seek(/* in_directionUp= */ false, /* in_skipSubChannel= */ false);
+
+ EXPECT_TRUE(result.isOk());
+ EXPECT_TIMEOUT_CALL_WAIT(*mCallback, onCurrentProgramInfoChangedMock, kTuneTimeoutSec);
+}
+
+/**
+ * Test seeking without tuner callback set.
+ *
+ * Verifies that:
+ * - No tuner callback set results in INVALID_STATE.
+ */
+TEST_P(BroadcastRadioHalTest, SeekFailsWithoutTunerCallback) {
+ LOG(DEBUG) << "SeekFailsWithoutTunerCallback Test";
+
+ mModule->unsetTunerCallback();
+
+ auto result = mModule->seek(/* in_directionUp= */ true, /* in_skipSubChannel= */ true);
+
+ EXPECT_EQ(result.getServiceSpecificError(), resultToInt(Result::INVALID_STATE));
+
+ result = mModule->seek(/* in_directionUp= */ false, /* in_skipSubChannel= */ false);
+
+ EXPECT_EQ(result.getServiceSpecificError(), resultToInt(Result::INVALID_STATE));
+}
+
+/**
+ * Test step operation.
+ *
+ * Verifies that:
+ * - the method succeeds or returns NOT_SUPPORTED;
+ * - the program info is changed within kTuneTimeoutSec if the method succeeded;
+ * - works both directions.
+ */
+TEST_P(BroadcastRadioHalTest, Step) {
+ LOG(DEBUG) << "Step Test";
+
+ EXPECT_TIMEOUT_CALL(*mCallback, onCurrentProgramInfoChangedMock, _).Times(AnyNumber());
+
+ auto result = mModule->step(/* in_directionUp= */ true);
+
+ if (result.getServiceSpecificError() == resultToInt(Result::NOT_SUPPORTED)) {
+ printSkipped("Step not supported");
+ return;
+ }
+ EXPECT_TRUE(result.isOk());
+ EXPECT_TIMEOUT_CALL_WAIT(*mCallback, onCurrentProgramInfoChangedMock, kTuneTimeoutSec);
+
+ EXPECT_TIMEOUT_CALL(*mCallback, onCurrentProgramInfoChangedMock, _).Times(AnyNumber());
+
+ result = mModule->step(/* in_directionUp= */ false);
+
+ EXPECT_TRUE(result.isOk());
+ EXPECT_TIMEOUT_CALL_WAIT(*mCallback, onCurrentProgramInfoChangedMock, kTuneTimeoutSec);
+}
+
+/**
+ * Test step operation without tuner callback set.
+ *
+ * Verifies that:
+ * - No tuner callback set results in INVALID_STATE.
+ */
+TEST_P(BroadcastRadioHalTest, StepFailsWithoutTunerCallback) {
+ LOG(DEBUG) << "StepFailsWithoutTunerCallback Test";
+
+ mModule->unsetTunerCallback();
+
+ auto result = mModule->step(/* in_directionUp= */ true);
+
+ EXPECT_EQ(result.getServiceSpecificError(), resultToInt(Result::INVALID_STATE));
+
+ result = mModule->step(/* in_directionUp= */ false);
+
+ EXPECT_EQ(result.getServiceSpecificError(), resultToInt(Result::INVALID_STATE));
+}
+
+/**
+ * Test tune cancellation.
+ *
+ * Verifies that:
+ * - the method does not crash after being invoked multiple times.
+ *
+ * Since cancel() might be called after the HAL completes an operation (tune, seek, and step)
+ * and before the callback completions, the operation might not be actually canceled and the
+ * effect of cancel() is not deterministic to be tested here.
+ */
+TEST_P(BroadcastRadioHalTest, Cancel) {
+ LOG(DEBUG) << "Cancel Test";
+
+ auto notSupportedError = resultToInt(Result::NOT_SUPPORTED);
+ for (int i = 0; i < 10; i++) {
+ auto result = mModule->seek(/* in_directionUp= */ true, /* in_skipSubChannel= */ true);
+
+ if (result.getServiceSpecificError() == notSupportedError) {
+ printSkipped("Cancel is skipped because of seek not supported");
+ return;
+ }
+ EXPECT_TRUE(result.isOk());
+
+ auto cancelResult = mModule->cancel();
+
+ ASSERT_TRUE(cancelResult.isOk());
+ }
+}
+
+/**
+ * Test IBroadcastRadio::get|setParameters() methods called with no parameters.
+ *
+ * Verifies that:
+ * - callback is called for empty parameters set.
+ */
+TEST_P(BroadcastRadioHalTest, NoParameters) {
+ LOG(DEBUG) << "NoParameters Test";
+
+ vector<VendorKeyValue> parametersResults = {};
+
+ auto halResult = mModule->setParameters({}, ¶metersResults);
+
+ ASSERT_TRUE(halResult.isOk());
+ ASSERT_EQ(parametersResults.size(), 0u);
+
+ parametersResults.clear();
+
+ halResult = mModule->getParameters({}, ¶metersResults);
+
+ ASSERT_TRUE(halResult.isOk());
+ ASSERT_EQ(parametersResults.size(), 0u);
+}
+
+/**
+ * Test IBroadcastRadio::get|setParameters() methods called with unknown parameters.
+ *
+ * Verifies that:
+ * - unknown parameters are ignored;
+ * - callback is called also for empty results set.
+ */
+TEST_P(BroadcastRadioHalTest, UnknownParameters) {
+ LOG(DEBUG) << "UnknownParameters Test";
+
+ vector<VendorKeyValue> parametersResults = {};
+
+ auto halResult =
+ mModule->setParameters({{"com.android.unknown", "sample"}}, ¶metersResults);
+
+ ASSERT_TRUE(halResult.isOk());
+ ASSERT_EQ(parametersResults.size(), 0u);
+
+ parametersResults.clear();
+
+ halResult = mModule->getParameters({"com.android.unknown*", "sample"}, ¶metersResults);
+
+ ASSERT_TRUE(halResult.isOk());
+ ASSERT_EQ(parametersResults.size(), 0u);
+}
+
+/**
+ * Test geting image of invalid ID.
+ *
+ * Verifies that:
+ * - getImage call handles argument 0 gracefully.
+ */
+TEST_P(BroadcastRadioHalTest, GetNoImage) {
+ LOG(DEBUG) << "GetNoImage Test";
+ vector<uint8_t> rawImage;
+
+ auto result = mModule->getImage(IBroadcastRadio::INVALID_IMAGE, &rawImage);
+
+ ASSERT_TRUE(result.isOk());
+ ASSERT_EQ(rawImage.size(), 0u);
+}
+
+/**
+ * Test getting config flags.
+ *
+ * Verifies that:
+ * - isConfigFlagSet either succeeds or ends with NOT_SUPPORTED or INVALID_STATE;
+ * - call success or failure is consistent with setConfigFlag.
+ */
+TEST_P(BroadcastRadioHalTest, FetchConfigFlags) {
+ LOG(DEBUG) << "FetchConfigFlags Test";
+
+ for (const auto& flag : kConfigFlagValues) {
+ bool gotValue = false;
+
+ auto halResult = mModule->isConfigFlagSet(flag, &gotValue);
+
+ if (halResult.getServiceSpecificError() != resultToInt(Result::NOT_SUPPORTED) &&
+ halResult.getServiceSpecificError() != resultToInt(Result::INVALID_STATE)) {
+ ASSERT_TRUE(halResult.isOk());
+ }
+
+ // set must fail or succeed the same way as get
+ auto setResult = mModule->setConfigFlag(flag, /* value= */ false);
+
+ EXPECT_TRUE((halResult.isOk() && setResult.isOk()) ||
+ (halResult.getServiceSpecificError()) == setResult.getServiceSpecificError());
+
+ setResult = mModule->setConfigFlag(flag, /* value= */ true);
+
+ EXPECT_TRUE((halResult.isOk() && setResult.isOk()) ||
+ (halResult.getServiceSpecificError()) == setResult.getServiceSpecificError());
+ }
+}
+
+/**
+ * Test setting config flags.
+ *
+ * Verifies that:
+ * - setConfigFlag either succeeds or ends with NOT_SUPPORTED or INVALID_STATE;
+ * - isConfigFlagSet reflects the state requested immediately after the set call.
+ */
+TEST_P(BroadcastRadioHalTest, SetConfigFlags) {
+ LOG(DEBUG) << "SetConfigFlags Test";
+
+ auto get = [&](ConfigFlag flag) -> bool {
+ bool* gotValue = nullptr;
+
+ auto halResult = mModule->isConfigFlagSet(flag, gotValue);
+
+ EXPECT_FALSE(gotValue == nullptr);
+ EXPECT_TRUE(halResult.isOk());
+ return *gotValue;
+ };
+
+ auto notSupportedError = resultToInt(Result::NOT_SUPPORTED);
+ auto invalidStateError = resultToInt(Result::INVALID_STATE);
+ for (const auto& flag : kConfigFlagValues) {
+ auto result = mModule->setConfigFlag(flag, /* value= */ false);
+
+ if (result.getServiceSpecificError() == notSupportedError ||
+ result.getServiceSpecificError() == invalidStateError) {
+ // setting to true must result in the same error as false
+ auto secondResult = mModule->setConfigFlag(flag, /* value= */ true);
+
+ EXPECT_TRUE((result.isOk() && secondResult.isOk()) ||
+ result.getServiceSpecificError() == secondResult.getServiceSpecificError());
+ continue;
+ } else {
+ ASSERT_TRUE(result.isOk());
+ }
+
+ // verify false is set
+ bool value = get(flag);
+ EXPECT_FALSE(value);
+
+ // try setting true this time
+ result = mModule->setConfigFlag(flag, /* value= */ true);
+
+ ASSERT_TRUE(result.isOk());
+ value = get(flag);
+ EXPECT_TRUE(value);
+
+ // false again
+ result = mModule->setConfigFlag(flag, /* value= */ false);
+
+ ASSERT_TRUE(result.isOk());
+ value = get(flag);
+ EXPECT_FALSE(value);
+ }
+}
+
+/**
+ * Test getting program list using empty program filter.
+ *
+ * Verifies that:
+ * - startProgramListUpdates either succeeds or returns NOT_SUPPORTED;
+ * - the complete list is fetched within kProgramListScanTimeoutSec;
+ * - stopProgramListUpdates does not crash.
+ */
+TEST_P(BroadcastRadioHalTest, GetProgramListFromEmptyFilter) {
+ LOG(DEBUG) << "GetProgramListFromEmptyFilter Test";
+
+ getProgramList();
+}
+
+/**
+ * Test getting program list using AMFM frequency program filter.
+ *
+ * Verifies that:
+ * - startProgramListUpdates either succeeds or returns NOT_SUPPORTED;
+ * - the complete list is fetched within kProgramListScanTimeoutSec;
+ * - stopProgramListUpdates does not crash;
+ * - result for startProgramListUpdates using a filter with AMFM_FREQUENCY_KHZ value of the first
+ * AMFM program matches the expected result.
+ */
+TEST_P(BroadcastRadioHalTest, GetProgramListFromAmFmFilter) {
+ LOG(DEBUG) << "GetProgramListFromAmFmFilter Test";
+
+ std::optional<bcutils::ProgramInfoSet> completeList = getProgramList();
+ if (!completeList) {
+ printSkipped("No program list available");
+ return;
+ }
+
+ ProgramFilter amfmFilter = {};
+ int expectedResultSize = 0;
+ uint64_t expectedFreq = 0;
+ for (const auto& program : *completeList) {
+ vector<int> amfmIds =
+ bcutils::getAllIds(program.selector, IdentifierType::AMFM_FREQUENCY_KHZ);
+ EXPECT_LE(amfmIds.size(), 1u);
+ if (amfmIds.size() == 0) {
+ continue;
+ }
+
+ if (expectedResultSize == 0) {
+ expectedFreq = amfmIds[0];
+ amfmFilter.identifiers = {
+ makeIdentifier(IdentifierType::AMFM_FREQUENCY_KHZ, expectedFreq)};
+ expectedResultSize = 1;
+ } else if (amfmIds[0] == expectedFreq) {
+ expectedResultSize++;
+ }
+ }
+
+ if (expectedResultSize == 0) {
+ printSkipped("No Am/FM programs available");
+ return;
+ }
+ std::optional<bcutils::ProgramInfoSet> amfmList = getProgramList(amfmFilter);
+ ASSERT_EQ(amfmList->size(), expectedResultSize) << "amfm filter result size is wrong";
+}
+
+/**
+ * Test getting program list using DAB ensemble program filter.
+ *
+ * Verifies that:
+ * - startProgramListUpdates either succeeds or returns NOT_SUPPORTED;
+ * - the complete list is fetched within kProgramListScanTimeoutSec;
+ * - stopProgramListUpdates does not crash;
+ * - result for startProgramListUpdates using a filter with DAB_ENSEMBLE value of the first DAB
+ * program matches the expected result.
+ */
+TEST_P(BroadcastRadioHalTest, GetProgramListFromDabFilter) {
+ LOG(DEBUG) << "GetProgramListFromDabFilter Test";
+
+ std::optional<bcutils::ProgramInfoSet> completeList = getProgramList();
+ if (!completeList) {
+ printSkipped("No program list available");
+ return;
+ }
+
+ ProgramFilter dabFilter = {};
+ int expectedResultSize = 0;
+ uint64_t expectedEnsemble = 0;
+ for (const auto& program : *completeList) {
+ auto dabEnsembles = bcutils::getAllIds(program.selector, IdentifierType::DAB_ENSEMBLE);
+ EXPECT_LE(dabEnsembles.size(), 1u);
+ if (dabEnsembles.size() == 0) {
+ continue;
+ }
+
+ if (expectedResultSize == 0) {
+ expectedEnsemble = dabEnsembles[0];
+ dabFilter.identifiers = {
+ makeIdentifier(IdentifierType::DAB_ENSEMBLE, expectedEnsemble)};
+ expectedResultSize = 1;
+ } else if (dabEnsembles[0] == expectedEnsemble) {
+ expectedResultSize++;
+ }
+ }
+
+ if (expectedResultSize == 0) {
+ printSkipped("No DAB programs available");
+ return;
+ }
+ std::optional<bcutils::ProgramInfoSet> dabList = getProgramList(dabFilter);
+ ASSERT_EQ(dabList->size(), expectedResultSize) << "dab filter result size is wrong";
+}
+
+/**
+ * Test HD_STATION_NAME correctness.
+ *
+ * Verifies that if a program on the list contains HD_STATION_NAME identifier:
+ * - the program provides station name in its metadata;
+ * - the identifier matches the name;
+ * - there is only one identifier of that type.
+ */
+TEST_P(BroadcastRadioHalTest, HdRadioStationNameId) {
+ LOG(DEBUG) << "HdRadioStationNameId Test";
+
+ std::optional<bcutils::ProgramInfoSet> list = getProgramList();
+ if (!list) {
+ printSkipped("No program list");
+ return;
+ }
+
+ for (const auto& program : *list) {
+ vector<int> nameIds = bcutils::getAllIds(program.selector, IdentifierType::HD_STATION_NAME);
+ EXPECT_LE(nameIds.size(), 1u);
+ if (nameIds.size() == 0) {
+ continue;
+ }
+
+ std::optional<string> name = bcutils::getMetadataString(program, Metadata::programName);
+ if (!name) {
+ name = bcutils::getMetadataString(program, Metadata::rdsPs);
+ }
+ ASSERT_TRUE(name.has_value());
+
+ ProgramIdentifier expectedId = bcutils::makeHdRadioStationName(*name);
+ EXPECT_EQ(nameIds[0], expectedId.value);
+ }
+}
+
+/**
+ * Test announcement listener registration.
+ *
+ * Verifies that:
+ * - registerAnnouncementListener either succeeds or returns NOT_SUPPORTED;
+ * - if it succeeds, it returns a valid close handle (which is a nullptr otherwise);
+ * - closing handle does not crash.
+ */
+TEST_P(BroadcastRadioHalTest, AnnouncementListenerRegistration) {
+ LOG(DEBUG) << "AnnouncementListenerRegistration Test";
+ std::shared_ptr<AnnouncementListenerMock> listener =
+ SharedRefBase::make<AnnouncementListenerMock>();
+ std::shared_ptr<ICloseHandle> closeHandle = nullptr;
+
+ auto halResult = mModule->registerAnnouncementListener(listener, {AnnouncementType::EMERGENCY},
+ &closeHandle);
+
+ if (halResult.getServiceSpecificError() == resultToInt(Result::NOT_SUPPORTED)) {
+ ASSERT_EQ(closeHandle.get(), nullptr);
+ printSkipped("Announcements not supported");
+ return;
+ }
+
+ ASSERT_TRUE(halResult.isOk());
+ ASSERT_NE(closeHandle.get(), nullptr);
+
+ closeHandle->close();
+}
+
+GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(BroadcastRadioHalTest);
+INSTANTIATE_TEST_SUITE_P(
+ PerInstance, BroadcastRadioHalTest,
+ testing::ValuesIn(::android::getAidlHalInstanceNames(IBroadcastRadio::descriptor)),
+ ::android::PrintInstanceNameToString);
+
+} // namespace aidl::android::hardware::broadcastradio::vts
+
+int main(int argc, char** argv) {
+ android::base::SetDefaultTag("BcRadio.vts");
+ android::base::SetMinimumLogSeverity(android::base::VERBOSE);
+ ::testing::InitGoogleTest(&argc, argv);
+ ABinderProcess_setThreadPoolMaxThreadCount(4);
+ ABinderProcess_startThreadPool();
+ return RUN_ALL_TESTS();
+}
diff --git a/broadcastradio/common/utilsaidl/Utils.cpp b/broadcastradio/common/utilsaidl/Utils.cpp
index a284651..52c7b40 100644
--- a/broadcastradio/common/utilsaidl/Utils.cpp
+++ b/broadcastradio/common/utilsaidl/Utils.cpp
@@ -19,6 +19,8 @@
#include "broadcastradio-utils-aidl/Utils.h"
#include <android-base/logging.h>
+#include <android-base/parseint.h>
+#include <android-base/strings.h>
#include <math/HashCombine.h>
@@ -28,6 +30,7 @@
namespace {
+using ::android::base::EqualsIgnoreCase;
using ::std::string;
using ::std::vector;
@@ -464,6 +467,71 @@
return makeIdentifier(IdentifierType::HD_STATION_NAME, val);
}
+IdentifierType getType(int typeAsInt) {
+ return static_cast<IdentifierType>(typeAsInt);
+}
+
+bool parseArgInt(const string& s, int* out) {
+ return ::android::base::ParseInt(s, out);
+}
+
+bool parseArgLong(const std::string& s, long* out) {
+ return ::android::base::ParseInt(s, out);
+}
+
+bool parseArgBool(const string& s, bool* out) {
+ if (EqualsIgnoreCase(s, "true")) {
+ *out = true;
+ } else if (EqualsIgnoreCase(s, "false")) {
+ *out = false;
+ } else {
+ return false;
+ }
+ return true;
+}
+
+bool parseArgDirection(const string& s, bool* out) {
+ if (EqualsIgnoreCase(s, "up")) {
+ *out = true;
+ } else if (EqualsIgnoreCase(s, "down")) {
+ *out = false;
+ } else {
+ return false;
+ }
+ return true;
+}
+
+bool parseArgIdentifierTypeArray(const string& s, vector<IdentifierType>* out) {
+ for (const string& val : ::android::base::Split(s, ",")) {
+ int outInt;
+ if (!parseArgInt(val, &outInt)) {
+ return false;
+ }
+ out->push_back(getType(outInt));
+ }
+ return true;
+}
+
+bool parseProgramIdentifierList(const std::string& s, vector<ProgramIdentifier>* out) {
+ for (const string& idStr : ::android::base::Split(s, ",")) {
+ const vector<string> idStrPair = ::android::base::Split(idStr, ":");
+ if (idStrPair.size() != 2) {
+ return false;
+ }
+ int idType;
+ if (!parseArgInt(idStrPair[0], &idType)) {
+ return false;
+ }
+ long idVal;
+ if (!parseArgLong(idStrPair[1], &idVal)) {
+ return false;
+ }
+ ProgramIdentifier id = {getType(idType), idVal};
+ out->push_back(id);
+ }
+ return true;
+}
+
} // namespace utils
utils::IdentifierIterator begin(const ProgramSelector& sel) {
diff --git a/broadcastradio/common/utilsaidl/include/broadcastradio-utils-aidl/Utils.h b/broadcastradio/common/utilsaidl/include/broadcastradio-utils-aidl/Utils.h
index c79c5c5..8ea6319 100644
--- a/broadcastradio/common/utilsaidl/include/broadcastradio-utils-aidl/Utils.h
+++ b/broadcastradio/common/utilsaidl/include/broadcastradio-utils-aidl/Utils.h
@@ -166,6 +166,20 @@
});
}
+IdentifierType getType(int typeAsInt);
+
+bool parseArgInt(const std::string& s, int* out);
+
+bool parseArgLong(const std::string& s, long* out);
+
+bool parseArgBool(const std::string& s, bool* out);
+
+bool parseArgDirection(const std::string& s, bool* out);
+
+bool parseArgIdentifierTypeArray(const std::string& s, std::vector<IdentifierType>* out);
+
+bool parseProgramIdentifierList(const std::string& s, std::vector<ProgramIdentifier>* out);
+
} // namespace utils
utils::IdentifierIterator begin(const ProgramSelector& sel);
diff --git a/camera/provider/2.4/vts/functional/VtsHalCameraProviderV2_4TargetTest.cpp b/camera/provider/2.4/vts/functional/VtsHalCameraProviderV2_4TargetTest.cpp
index 6866776..b0ae20e 100644
--- a/camera/provider/2.4/vts/functional/VtsHalCameraProviderV2_4TargetTest.cpp
+++ b/camera/provider/2.4/vts/functional/VtsHalCameraProviderV2_4TargetTest.cpp
@@ -7883,6 +7883,7 @@
}
}
+ camera_metadata_t* staticMetadata;
camera_metadata_ro_entry physicalMultiResStreamConfigs;
camera_metadata_ro_entry physicalStreamConfigs;
camera_metadata_ro_entry physicalMaxResolutionStreamConfigs;
@@ -7901,8 +7902,9 @@
ret = subDevice->getCameraCharacteristics([&](auto status, const auto& chars) {
ASSERT_EQ(Status::OK, status);
- const camera_metadata_t* staticMetadata =
- reinterpret_cast<const camera_metadata_t*>(chars.data());
+ staticMetadata = clone_camera_metadata(
+ reinterpret_cast<const camera_metadata_t*>(chars.data()));
+ ASSERT_NE(nullptr, staticMetadata);
rc = getSystemCameraKind(staticMetadata, &physSystemCameraKind);
ASSERT_EQ(rc, Status::OK);
// Make sure that the system camera kind of a non-hidden
@@ -7936,7 +7938,9 @@
verifyCameraCharacteristics(status, chars);
verifyMonochromeCharacteristics(chars, deviceVersion);
- auto staticMetadata = (const camera_metadata_t*)chars.data();
+ staticMetadata = clone_camera_metadata(
+ reinterpret_cast<const camera_metadata_t*>(chars.data()));
+ ASSERT_NE(nullptr, staticMetadata);
retcode = find_camera_metadata_ro_entry(
staticMetadata, ANDROID_CONTROL_ZOOM_RATIO_RANGE, &entry);
bool subCameraHasZoomRatioRange = (0 == retcode && entry.count == 2);
@@ -8064,6 +8068,7 @@
}
}
}
+ free_camera_metadata(staticMetadata);
}
// If a multi-resolution stream is supported, there must be at least one
diff --git a/camera/provider/aidl/vts/camera_aidl_test.cpp b/camera/provider/aidl/vts/camera_aidl_test.cpp
index 20f32bf..2c2f1b2 100644
--- a/camera/provider/aidl/vts/camera_aidl_test.cpp
+++ b/camera/provider/aidl/vts/camera_aidl_test.cpp
@@ -1181,6 +1181,7 @@
camera_metadata_ro_entry physicalMultiResStreamConfigs;
camera_metadata_ro_entry physicalStreamConfigs;
camera_metadata_ro_entry physicalMaxResolutionStreamConfigs;
+ CameraMetadata physChars;
bool isUltraHighRes = false;
std::unordered_set<int32_t> subCameraPrivacyTestPatterns;
if (isPublicId) {
@@ -1189,12 +1190,11 @@
ASSERT_TRUE(ret.isOk());
ASSERT_NE(subDevice, nullptr);
- CameraMetadata subDeviceChars;
- ret = subDevice->getCameraCharacteristics(&subDeviceChars);
+ ret = subDevice->getCameraCharacteristics(&physChars);
ASSERT_TRUE(ret.isOk());
const camera_metadata_t* staticMetadata =
- reinterpret_cast<const camera_metadata_t*>(subDeviceChars.metadata.data());
+ reinterpret_cast<const camera_metadata_t*>(physChars.metadata.data());
retStatus = getSystemCameraKind(staticMetadata, &physSystemCameraKind);
ASSERT_EQ(retStatus, Status::OK);
@@ -1215,7 +1215,6 @@
getPrivacyTestPatternModes(staticMetadata, &subCameraPrivacyTestPatterns);
} else {
// Check camera characteristics for hidden camera id
- CameraMetadata physChars;
ndk::ScopedAStatus ret =
device->getPhysicalCameraCharacteristics(physicalId, &physChars);
ASSERT_TRUE(ret.isOk());
diff --git a/identity/aidl/default/Android.bp b/identity/aidl/default/Android.bp
index 31ab400..a57875a 100644
--- a/identity/aidl/default/Android.bp
+++ b/identity/aidl/default/Android.bp
@@ -10,6 +10,10 @@
cc_library_static {
name: "android.hardware.identity-libeic-hal-common",
vendor_available: true,
+ defaults: [
+ "identity_use_latest_hal_aidl_ndk_static",
+ "keymint_use_latest_hal_aidl_ndk_static",
+ ],
srcs: [
"common/IdentityCredential.cpp",
"common/IdentityCredentialStore.cpp",
@@ -40,9 +44,7 @@
"libsoft_attestation_cert",
"libpuresoftkeymasterdevice",
"android.hardware.identity-support-lib",
- "android.hardware.identity-V4-ndk",
"android.hardware.keymaster-V3-ndk",
- "android.hardware.security.keymint-V2-ndk",
],
}
@@ -83,6 +85,7 @@
vintf_fragments: ["identity-default.xml"],
vendor: true,
defaults: [
+ "identity_use_latest_hal_aidl_ndk_static",
"keymint_use_latest_hal_aidl_ndk_static",
],
cflags: [
@@ -106,7 +109,6 @@
"libsoft_attestation_cert",
"libpuresoftkeymasterdevice",
"android.hardware.identity-support-lib",
- "android.hardware.identity-V4-ndk",
"android.hardware.keymaster-V3-ndk",
"android.hardware.identity-libeic-hal-common",
"android.hardware.identity-libeic-library",
diff --git a/identity/aidl/vts/Android.bp b/identity/aidl/vts/Android.bp
index 51ab110..dd29819 100644
--- a/identity/aidl/vts/Android.bp
+++ b/identity/aidl/vts/Android.bp
@@ -11,6 +11,7 @@
name: "VtsHalIdentityTargetTest",
defaults: [
"VtsHalTargetTestDefaults",
+ "identity_use_latest_hal_aidl_cpp_static",
"keymint_use_latest_hal_aidl_cpp_static",
"keymint_use_latest_hal_aidl_ndk_static",
"use_libaidlvintf_gtest_helper_static",
@@ -46,7 +47,6 @@
"libpuresoftkeymasterdevice",
"android.hardware.keymaster@4.0",
"android.hardware.identity-support-lib",
- "android.hardware.identity-V4-cpp",
"android.hardware.keymaster-V3-cpp",
"android.hardware.keymaster-V3-ndk",
"libkeymaster4support",
diff --git a/radio/aidl/aidl_api/android.hardware.radio.network/current/android/hardware/radio/network/EmergencyRegResult.aidl b/radio/aidl/aidl_api/android.hardware.radio.network/current/android/hardware/radio/network/EmergencyRegResult.aidl
index 523d1aa..af17b43 100644
--- a/radio/aidl/aidl_api/android.hardware.radio.network/current/android/hardware/radio/network/EmergencyRegResult.aidl
+++ b/radio/aidl/aidl_api/android.hardware.radio.network/current/android/hardware/radio/network/EmergencyRegResult.aidl
@@ -40,6 +40,6 @@
boolean isEmcBearerSupported;
byte nwProvidedEmc;
byte nwProvidedEmf;
- String mcc;
- String mnc;
+ String mcc = "";
+ String mnc = "";
}
diff --git a/radio/aidl/android/hardware/radio/network/EmergencyRegResult.aidl b/radio/aidl/android/hardware/radio/network/EmergencyRegResult.aidl
index c22317a..ec70e17 100644
--- a/radio/aidl/android/hardware/radio/network/EmergencyRegResult.aidl
+++ b/radio/aidl/android/hardware/radio/network/EmergencyRegResult.aidl
@@ -16,8 +16,8 @@
package android.hardware.radio.network;
import android.hardware.radio.AccessNetwork;
-import android.hardware.radio.network.RegState;
import android.hardware.radio.network.Domain;
+import android.hardware.radio.network.RegState;
@VintfStability
@JavaDerive(toString=true)
@@ -56,8 +56,8 @@
byte nwProvidedEmf;
/** 3-digit Mobile Country Code, 000..999, empty string if unknown. */
- String mcc;
+ String mcc = "";
/** 2 or 3-digit Mobile Network Code, 00..999, empty string if unknown. */
- String mnc;
+ String mnc = "";
}