diff options
56 files changed, 1747 insertions, 630 deletions
diff --git a/audio/core/all-versions/vts/functional/AudioPrimaryHidlHalTest.h b/audio/core/all-versions/vts/functional/AudioPrimaryHidlHalTest.h index 09446cd8ca..38d6eff7d0 100644 --- a/audio/core/all-versions/vts/functional/AudioPrimaryHidlHalTest.h +++ b/audio/core/all-versions/vts/functional/AudioPrimaryHidlHalTest.h @@ -34,6 +34,7 @@ #include <hwbinder/IPCThreadState.h> +#include <android-base/expected.h> #include <android-base/logging.h> #include <system/audio_config.h> @@ -130,6 +131,33 @@ class HidlTest : public ::testing::Test { using IDevice = ::android::hardware::audio::CPP_VERSION::IDevice; using IDevicesFactory = ::android::hardware::audio::CPP_VERSION::IDevicesFactory; + static android::base::expected<std::vector<std::string>, std::string> getAllFactoryInstances() { + using ::android::hardware::audio::CPP_VERSION::IDevicesFactory; + const std::string factoryDescriptor = IDevicesFactory::descriptor; + // Make sure that the instance is the exact minor version. + // Using a 7.1 factory for 7.0 test is not always possible because + // 7.1 can be configured via the XML config to use features that are + // absent in 7.0. + auto instances = ::android::hardware::getAllHalInstanceNames(factoryDescriptor); + if (instances.empty()) return instances; + // Use the default instance for checking the implementation version. + auto defaultInstance = IDevicesFactory::getService("default"); + if (defaultInstance == nullptr) { + return ::android::base::unexpected("Failed to obtain IDevicesFactory/default"); + } + std::string actualDescriptor; + auto intDescRet = defaultInstance->interfaceDescriptor( + [&](const auto& descriptor) { actualDescriptor = descriptor; }); + if (!intDescRet.isOk()) { + return ::android::base::unexpected("Failed to obtain interface descriptor: " + + intDescRet.description()); + } + if (factoryDescriptor == actualDescriptor) + return instances; + else + return {}; + } + virtual ~HidlTest() = default; // public access to avoid annoyances when using this method in template classes // derived from test classes @@ -174,9 +202,11 @@ const PolicyConfig& getCachedPolicyConfig() { } TEST(CheckConfig, audioPolicyConfigurationValidation) { - const auto factories = ::android::hardware::getAllHalInstanceNames( - ::android::hardware::audio::CPP_VERSION::IDevicesFactory::descriptor); - if (factories.size() == 0) { + const auto factories = HidlTest::getAllFactoryInstances(); + if (!factories.ok()) { + FAIL() << factories.error(); + } + if (factories.value().size() == 0) { GTEST_SKIP() << "Skipping audioPolicyConfigurationValidation because no factory instances " "are found."; } @@ -205,11 +235,11 @@ static inline std::string DeviceParameterToString( const std::vector<DeviceParameter>& getDeviceParameters() { static std::vector<DeviceParameter> parameters = [] { std::vector<DeviceParameter> result; - const auto factories = ::android::hardware::getAllHalInstanceNames( - ::android::hardware::audio::CPP_VERSION::IDevicesFactory::descriptor); + const auto factories = HidlTest::getAllFactoryInstances(); + if (!factories.ok()) return result; const auto devices = getCachedPolicyConfig().getModulesWithDevicesNames(); result.reserve(devices.size()); - for (const auto& factoryName : factories) { + for (const auto& factoryName : factories.value()) { for (const auto& deviceName : devices) { if (DeviceManager::getInstance().get(factoryName, deviceName) != nullptr) { result.emplace_back(factoryName, deviceName); @@ -224,9 +254,9 @@ const std::vector<DeviceParameter>& getDeviceParameters() { const std::vector<DeviceParameter>& getDeviceParametersForFactoryTests() { static std::vector<DeviceParameter> parameters = [] { std::vector<DeviceParameter> result; - const auto factories = ::android::hardware::getAllHalInstanceNames( - ::android::hardware::audio::CPP_VERSION::IDevicesFactory::descriptor); - for (const auto& factoryName : factories) { + const auto factories = HidlTest::getAllFactoryInstances(); + if (!factories.ok()) return result; + for (const auto& factoryName : factories.value()) { result.emplace_back(factoryName, DeviceManager::getInstance().getPrimary(factoryName) != nullptr ? DeviceManager::kPrimaryDevice diff --git a/automotive/audiocontrol/aidl/default/Android.bp b/automotive/audiocontrol/aidl/default/Android.bp index 1439cce606..6bf4b9d860 100644 --- a/automotive/audiocontrol/aidl/default/Android.bp +++ b/automotive/audiocontrol/aidl/default/Android.bp @@ -29,8 +29,9 @@ cc_binary { vendor: true, shared_libs: [ "android.hardware.audio.common@7.0-enums", + "android.hardware.audio.common-V1-ndk", "android.frameworks.automotive.powerpolicy-V1-ndk", - "android.hardware.automotive.audiocontrol-V1-ndk", + "android.hardware.automotive.audiocontrol-V2-ndk", "libbase", "libbinder_ndk", "libcutils", diff --git a/automotive/audiocontrol/aidl/default/AudioControl.cpp b/automotive/audiocontrol/aidl/default/AudioControl.cpp index c0bc796cdf..a121f8be21 100644 --- a/automotive/audiocontrol/aidl/default/AudioControl.cpp +++ b/automotive/audiocontrol/aidl/default/AudioControl.cpp @@ -30,6 +30,8 @@ #include <android_audio_policy_configuration_V7_0-enums.h> #include <private/android_filesystem_config.h> +#include <numeric> + #include <stdio.h> namespace aidl::android::hardware::automotive::audiocontrol { @@ -147,6 +149,47 @@ ndk::ScopedAStatus AudioControl::onDevicesToMuteChange( return ndk::ScopedAStatus::ok(); } +template <typename aidl_type> +static inline std::string toString(const std::vector<aidl_type>& in_values) { + return std::accumulate(std::begin(in_values), std::end(in_values), std::string{}, + [](std::string& ls, const aidl_type& rs) { + return ls += (ls.empty() ? "" : ",") + rs.toString(); + }); +} +template <typename aidl_enum_type> +static inline std::string toEnumString(const std::vector<aidl_enum_type>& in_values) { + return std::accumulate(std::begin(in_values), std::end(in_values), std::string{}, + [](std::string& ls, const aidl_enum_type& rs) { + return ls += (ls.empty() ? "" : ",") + toString(rs); + }); +} + +ndk::ScopedAStatus AudioControl::onAudioFocusChangeWithMetaData( + const audiohalcommon::PlaybackTrackMetadata& in_playbackMetaData, int32_t in_zoneId, + AudioFocusChange in_focusChange) { + LOG(INFO) << "Focus changed: " << toString(in_focusChange).c_str() << " for metadata " + << in_playbackMetaData.toString().c_str() << " in zone " << in_zoneId; + return ndk::ScopedAStatus::ok(); +} + +ndk::ScopedAStatus AudioControl::setAudioDeviceGainsChanged( + const std::vector<Reasons>& in_reasons, const std::vector<AudioGainConfigInfo>& in_gains) { + LOG(INFO) << "Audio Device Gains changed: resons:" << toEnumString(in_reasons).c_str() + << " for devices: " << toString(in_gains).c_str(); + return ndk::ScopedAStatus::ok(); +} + +ndk::ScopedAStatus AudioControl::registerGainCallback( + const std::shared_ptr<IAudioGainCallback>& in_callback) { + LOG(DEBUG) << ": " << __func__; + if (in_callback) { + std::atomic_store(&mAudioGainCallback, in_callback); + } else { + LOG(ERROR) << "Unexpected nullptr for audio gain callback resulting in no-op."; + } + return ndk::ScopedAStatus::ok(); +} + binder_status_t AudioControl::dump(int fd, const char** args, uint32_t numArgs) { if (numArgs == 0) { return dumpsys(fd); @@ -159,6 +202,12 @@ binder_status_t AudioControl::dump(int fd, const char** args, uint32_t numArgs) return cmdRequestFocus(fd, args, numArgs); } else if (EqualsIgnoreCase(option, "--abandon")) { return cmdAbandonFocus(fd, args, numArgs); + } else if (EqualsIgnoreCase(option, "--requestFocusWithMetaData")) { + return cmdRequestFocusWithMetaData(fd, args, numArgs); + } else if (EqualsIgnoreCase(option, "--abandonFocusWithMetaData")) { + return cmdAbandonFocusWithMetaData(fd, args, numArgs); + } else if (EqualsIgnoreCase(option, "--audioGainCallback")) { + return cmdOnAudioDeviceGainsChanged(fd, args, numArgs); } else { dprintf(fd, "Invalid option: %s\n", option.c_str()); return STATUS_BAD_VALUE; @@ -171,20 +220,49 @@ binder_status_t AudioControl::dumpsys(int fd) { } else { dprintf(fd, "Focus listener registered\n"); } + dprintf(fd, "AudioGainCallback %sregistered\n", (mAudioGainCallback == nullptr ? "NOT " : "")); return STATUS_OK; } binder_status_t AudioControl::cmdHelp(int fd) const { dprintf(fd, "Usage: \n\n"); - dprintf(fd, "[no args]: dumps focus listener status\n"); + dprintf(fd, "[no args]: dumps focus listener / gain callback registered status\n"); dprintf(fd, "--help: shows this help\n"); dprintf(fd, "--request <USAGE> <ZONE_ID> <FOCUS_GAIN>: requests audio focus for specified " - "usage (string), audio zone ID (int), and focus gain type (int)\n"); + "usage (string), audio zone ID (int), and focus gain type (int)\n" + "Deprecated, use MetaData instead\n"); dprintf(fd, "--abandon <USAGE> <ZONE_ID>: abandons audio focus for specified usage (string) and " - "audio zone ID (int)\n"); + "audio zone ID (int)\n" + "Deprecated, use MetaData instead\n"); dprintf(fd, "See audio_policy_configuration.xsd for valid AudioUsage values.\n"); + + dprintf(fd, + "--requestFocusWithMetaData <METADATA> <ZONE_ID> <FOCUS_GAIN>: " + "requests audio focus for specified metadata, audio zone ID (int), " + "and focus gain type (int)\n"); + dprintf(fd, + "--abandonFocusWithMetaData <METADATA> <ZONE_ID>: " + "abandons audio focus for specified metadata and audio zone ID (int)\n"); + dprintf(fd, + "--audioGainCallback <ZONE_ID> <REASON_1>[,<REASON_N> ...]" + "<DEVICE_ADDRESS_1> <GAIN_INDEX_1> [<DEVICE_ADDRESS_N> <GAIN_INDEX_N> ...]: fire audio " + "gain callback for audio zone ID (int), the given reasons (csv int) for given pairs " + "of device address (string) and gain index (int) \n"); + + dprintf(fd, + "Note on <METADATA>: <USAGE,CONTENT_TYPE[,TAGS]> specified as where (int)usage, " + "(int)content type and tags (string)string)\n"); + dprintf(fd, + "See android/media/audio/common/AudioUsageType.aidl for valid AudioUsage values.\n"); + dprintf(fd, + "See android/media/audio/common/AudioContentType.aidl for valid AudioContentType " + "values.\n"); + dprintf(fd, + "Tags are optional. If provided, it must follow the <key>=<value> pattern, where the " + "value is namespaced (for example com.google.strategy=VR).\n"); + return STATUS_OK; } @@ -266,4 +344,174 @@ binder_status_t AudioControl::cmdAbandonFocus(int fd, const char** args, uint32_ return STATUS_OK; } +binder_status_t AudioControl::parseMetaData(int fd, const std::string& metadataLiteral, + audiohalcommon::PlaybackTrackMetadata& trackMetadata) { + std::stringstream csvMetaData(metadataLiteral); + std::vector<std::string> splitMetaData; + std::string attribute; + while (getline(csvMetaData, attribute, ',')) { + splitMetaData.push_back(attribute); + } + if (splitMetaData.size() != 2 && splitMetaData.size() != 3) { + dprintf(fd, + "Invalid metadata: %s, please provide <METADATA> as <USAGE,CONTENT_TYPE[,TAGS]> " + "where (int)usage, (int)content type and tags (string)string)\n", + metadataLiteral.c_str()); + return STATUS_BAD_VALUE; + } + int usage; + if (!safelyParseInt(splitMetaData[0], &usage)) { + dprintf(fd, "Non-integer usage provided with request: %s\n", splitMetaData[0].c_str()); + return STATUS_BAD_VALUE; + } + int contentType; + if (!safelyParseInt(splitMetaData[1], &contentType)) { + dprintf(fd, "Non-integer content type provided with request: %s\n", + splitMetaData[1].c_str()); + return STATUS_BAD_VALUE; + } + const std::string tags = (splitMetaData.size() == 3 ? splitMetaData[2] : ""); + + trackMetadata = {.usage = static_cast<audiomediacommon::AudioUsage>(usage), + .contentType = static_cast<audiomediacommon::AudioContentType>(contentType), + .tags = {tags}}; + return STATUS_OK; +} + +binder_status_t AudioControl::cmdRequestFocusWithMetaData(int fd, const char** args, + uint32_t numArgs) { + if (!checkCallerHasWritePermissions(fd)) { + return STATUS_PERMISSION_DENIED; + } + if (numArgs != 4) { + dprintf(fd, + "Invalid number of arguments: please provide:\n" + "--requestFocusWithMetaData <METADATA> <ZONE_ID> <FOCUS_GAIN>: " + "requests audio focus for specified metadata, audio zone ID (int), " + "and focus gain type (int)\n"); + return STATUS_BAD_VALUE; + } + std::string metadataLiteral = std::string(args[1]); + audiohalcommon::PlaybackTrackMetadata trackMetadata{}; + auto status = parseMetaData(fd, metadataLiteral, trackMetadata); + if (status != STATUS_OK) { + return status; + } + + int zoneId; + if (!safelyParseInt(std::string(args[2]), &zoneId)) { + dprintf(fd, "Non-integer zoneId provided with request: %s\n", std::string(args[2]).c_str()); + return STATUS_BAD_VALUE; + } + + int focusGainValue; + if (!safelyParseInt(std::string(args[3]), &focusGainValue)) { + dprintf(fd, "Non-integer focusGain provided with request: %s\n", + std::string(args[3]).c_str()); + return STATUS_BAD_VALUE; + } + AudioFocusChange focusGain = AudioFocusChange(focusGainValue); + + if (mFocusListener == nullptr) { + dprintf(fd, "Unable to request focus - no focus listener registered\n"); + return STATUS_BAD_VALUE; + } + mFocusListener->requestAudioFocusWithMetaData(trackMetadata, zoneId, focusGain); + dprintf(fd, "Requested focus for metadata %s, zoneId %d, and focusGain %d\n", + trackMetadata.toString().c_str(), zoneId, focusGain); + return STATUS_OK; +} + +binder_status_t AudioControl::cmdAbandonFocusWithMetaData(int fd, const char** args, + uint32_t numArgs) { + if (!checkCallerHasWritePermissions(fd)) { + return STATUS_PERMISSION_DENIED; + } + if (numArgs != 3) { + dprintf(fd, + "Invalid number of arguments: please provide:\n" + "--abandonFocusWithMetaData <METADATA> <ZONE_ID>: " + "abandons audio focus for specified metadata and audio zone ID (int)\n"); + return STATUS_BAD_VALUE; + } + std::string metadataLiteral = std::string(args[1]); + audiohalcommon::PlaybackTrackMetadata trackMetadata{}; + auto status = parseMetaData(fd, metadataLiteral, trackMetadata); + if (status != STATUS_OK) { + return status; + } + int zoneId; + if (!safelyParseInt(std::string(args[2]), &zoneId)) { + dprintf(fd, "Non-integer zoneId provided with request: %s\n", std::string(args[2]).c_str()); + return STATUS_BAD_VALUE; + } + if (mFocusListener == nullptr) { + dprintf(fd, "Unable to abandon focus - no focus listener registered\n"); + return STATUS_BAD_VALUE; + } + + mFocusListener->abandonAudioFocusWithMetaData(trackMetadata, zoneId); + dprintf(fd, "Abandoned focus for metadata %s and zoneId %d\n", trackMetadata.toString().c_str(), + zoneId); + return STATUS_OK; +} + +binder_status_t AudioControl::cmdOnAudioDeviceGainsChanged(int fd, const char** args, + uint32_t numArgs) { + if (!checkCallerHasWritePermissions(fd)) { + return STATUS_PERMISSION_DENIED; + } + if ((numArgs + 1) % 2 != 0) { + dprintf(fd, + "Invalid number of arguments: please provide\n" + "--audioGainCallback <ZONE_ID> <REASON_1>[,<REASON_N> ...]" + "<DEVICE_ADDRESS_1> <GAIN_INDEX_1> [<DEVICE_ADDRESS_N> <GAIN_INDEX_N> ...]: " + "fire audio gain callback for audio zone ID (int), " + "with the given reasons (csv int) for given pairs of device address (string) " + "and gain index (int) \n"); + return STATUS_BAD_VALUE; + } + int zoneId; + if (!safelyParseInt(string(args[1]), &zoneId)) { + dprintf(fd, "Non-integer zoneId provided with request: %s\n", std::string(args[1]).c_str()); + return STATUS_BAD_VALUE; + } + std::string reasonsLiteral = std::string(args[2]); + std::stringstream csvReasonsLiteral(reasonsLiteral); + std::vector<Reasons> reasons; + std::string reasonLiteral; + while (getline(csvReasonsLiteral, reasonLiteral, ',')) { + int reason; + if (!safelyParseInt(reasonLiteral, &reason)) { + dprintf(fd, "Invalid Reason(s) provided %s\n", reasonLiteral.c_str()); + return STATUS_BAD_VALUE; + } + reasons.push_back(static_cast<Reasons>(reason)); + } + + std::vector<AudioGainConfigInfo> agcis{}; + for (uint32_t i = 3; i < numArgs; i += 2) { + std::string deviceAddress = std::string(args[i]); + int32_t index; + if (!safelyParseInt(std::string(args[i + 1]), &index)) { + dprintf(fd, "Non-integer index provided with request: %s\n", + std::string(args[i + 1]).c_str()); + return STATUS_BAD_VALUE; + } + AudioGainConfigInfo agci{zoneId, deviceAddress, index}; + agcis.push_back(agci); + } + if (mAudioGainCallback == nullptr) { + dprintf(fd, + "Unable to trig audio gain callback for reasons=%s and gains=%s\n" + " - no audio gain callback registered\n", + toEnumString(reasons).c_str(), toString(agcis).c_str()); + return STATUS_BAD_VALUE; + } + + mAudioGainCallback->onAudioDeviceGainsChanged(reasons, agcis); + dprintf(fd, "Fired audio gain callback for reasons=%s and gains=%s\n", + toEnumString(reasons).c_str(), toString(agcis).c_str()); + return STATUS_OK; +} } // namespace aidl::android::hardware::automotive::audiocontrol diff --git a/automotive/audiocontrol/aidl/default/AudioControl.h b/automotive/audiocontrol/aidl/default/AudioControl.h index cca9c44004..16b80e890c 100644 --- a/automotive/audiocontrol/aidl/default/AudioControl.h +++ b/automotive/audiocontrol/aidl/default/AudioControl.h @@ -17,12 +17,20 @@ #define ANDROID_HARDWARE_AUTOMOTIVE_AUDIOCONTROL_AUDIOCONTROL_H #include <aidl/android/hardware/automotive/audiocontrol/AudioFocusChange.h> +#include <aidl/android/hardware/automotive/audiocontrol/AudioGainConfigInfo.h> #include <aidl/android/hardware/automotive/audiocontrol/BnAudioControl.h> #include <aidl/android/hardware/automotive/audiocontrol/DuckingInfo.h> +#include <aidl/android/hardware/automotive/audiocontrol/IAudioGainCallback.h> #include <aidl/android/hardware/automotive/audiocontrol/MutingInfo.h> +#include <aidl/android/hardware/automotive/audiocontrol/Reasons.h> + +#include <aidl/android/hardware/audio/common/PlaybackTrackMetadata.h> namespace aidl::android::hardware::automotive::audiocontrol { +namespace audiohalcommon = ::aidl::android::hardware::audio::common; +namespace audiomediacommon = ::aidl::android::media::audio::common; + class AudioControl : public BnAudioControl { public: ndk::ScopedAStatus onAudioFocusChange(const std::string& in_usage, int32_t in_zoneId, @@ -35,6 +43,15 @@ class AudioControl : public BnAudioControl { const std::shared_ptr<IFocusListener>& in_listener) override; ndk::ScopedAStatus setBalanceTowardRight(float in_value) override; ndk::ScopedAStatus setFadeTowardFront(float in_value) override; + ndk::ScopedAStatus onAudioFocusChangeWithMetaData( + const audiohalcommon::PlaybackTrackMetadata& in_playbackMetaData, int32_t in_zoneId, + AudioFocusChange in_focusChange) override; + ndk::ScopedAStatus setAudioDeviceGainsChanged( + const std::vector<Reasons>& in_reasons, + const std::vector<AudioGainConfigInfo>& in_gains) override; + ndk::ScopedAStatus registerGainCallback( + const std::shared_ptr<IAudioGainCallback>& in_callback) override; + binder_status_t dump(int fd, const char** args, uint32_t numArgs) override; private: @@ -44,9 +61,22 @@ class AudioControl : public BnAudioControl { // listener, then it should also include mutexes or make the listener atomic. std::shared_ptr<IFocusListener> mFocusListener; + /** + * @brief mAudioGainCallback will be used by this HAL instance to communicate e.g. with a single + * instance of CarAudioService to report unexpected gain changed. + */ + std::shared_ptr<IAudioGainCallback> mAudioGainCallback = nullptr; + binder_status_t cmdHelp(int fd) const; binder_status_t cmdRequestFocus(int fd, const char** args, uint32_t numArgs); binder_status_t cmdAbandonFocus(int fd, const char** args, uint32_t numArgs); + binder_status_t cmdRequestFocusWithMetaData(int fd, const char** args, uint32_t numArgs); + binder_status_t cmdAbandonFocusWithMetaData(int fd, const char** args, uint32_t numArgs); + binder_status_t cmdOnAudioDeviceGainsChanged(int fd, const char** args, uint32_t numArgs); + + binder_status_t parseMetaData(int fd, const std::string& metadataLiteral, + audiohalcommon::PlaybackTrackMetadata& trackMetadata); + binder_status_t dumpsys(int fd); }; diff --git a/automotive/audiocontrol/aidl/default/audiocontrol-default.xml b/automotive/audiocontrol/aidl/default/audiocontrol-default.xml index f95d05fe77..e82f6fada9 100644 --- a/automotive/audiocontrol/aidl/default/audiocontrol-default.xml +++ b/automotive/audiocontrol/aidl/default/audiocontrol-default.xml @@ -1,4 +1,4 @@ -<manifest version="1.0" type="device"> +<manifest version="2.0" type="device"> <hal format="aidl"> <name>android.hardware.automotive.audiocontrol</name> <fqname>IAudioControl/default</fqname> diff --git a/automotive/audiocontrol/aidl/vts/Android.bp b/automotive/audiocontrol/aidl/vts/Android.bp index 6856b91d2b..3d4be4828f 100644 --- a/automotive/audiocontrol/aidl/vts/Android.bp +++ b/automotive/audiocontrol/aidl/vts/Android.bp @@ -37,11 +37,16 @@ cc_test { "libxml2", ], static_libs: [ - "android.hardware.automotive.audiocontrol-V1-cpp", + "android.hardware.automotive.audiocontrol-V2-cpp", + "android.hardware.audio.common-V1-cpp", + "android.media.audio.common.types-V1-cpp", "libgmock", ], test_suites: [ "general-tests", "vts", ], + cflags: [ + "-Wno-deprecated-declarations", + ], } diff --git a/automotive/audiocontrol/aidl/vts/VtsHalAudioControlTargetTest.cpp b/automotive/audiocontrol/aidl/vts/VtsHalAudioControlTargetTest.cpp index ae53c68528..f4f5eef757 100644 --- a/automotive/audiocontrol/aidl/vts/VtsHalAudioControlTargetTest.cpp +++ b/automotive/audiocontrol/aidl/vts/VtsHalAudioControlTargetTest.cpp @@ -19,6 +19,7 @@ #include <aidl/Vintf.h> #include <gmock/gmock.h> +#include <android/hardware/automotive/audiocontrol/BnAudioGainCallback.h> #include <android/hardware/automotive/audiocontrol/BnFocusListener.h> #include <android/hardware/automotive/audiocontrol/IAudioControl.h> #include <android/log.h> @@ -30,10 +31,13 @@ using android::sp; using android::String16; using android::binder::Status; using android::hardware::automotive::audiocontrol::AudioFocusChange; +using android::hardware::automotive::audiocontrol::AudioGainConfigInfo; +using android::hardware::automotive::audiocontrol::BnAudioGainCallback; using android::hardware::automotive::audiocontrol::BnFocusListener; using android::hardware::automotive::audiocontrol::DuckingInfo; using android::hardware::automotive::audiocontrol::IAudioControl; using android::hardware::automotive::audiocontrol::MutingInfo; +using android::hardware::automotive::audiocontrol::Reasons; #include "android_audio_policy_configuration_V7_0.h" @@ -41,6 +45,9 @@ namespace xsd { using namespace android::audio::policy::configuration::V7_0; } +namespace audiohalcommon = android::hardware::audio::common; +namespace audiomediacommon = android::media::audio::common; + class AudioControlAidl : public testing::TestWithParam<std::string> { public: virtual void SetUp() override { @@ -103,6 +110,11 @@ struct FocusListenerMock : BnFocusListener { MOCK_METHOD(Status, requestAudioFocus, (const String16& usage, int32_t zoneId, AudioFocusChange focusGain)); MOCK_METHOD(Status, abandonAudioFocus, (const String16& usage, int32_t zoneId)); + MOCK_METHOD(Status, requestAudioFocusWithMetaData, + (const audiohalcommon::PlaybackTrackMetadata& metaData, int32_t zoneId, + AudioFocusChange focusGain)); + MOCK_METHOD(Status, abandonAudioFocusWithMetaData, + (const audiohalcommon::PlaybackTrackMetadata& metaData, int32_t zoneId)); }; /* @@ -159,6 +171,61 @@ TEST_P(AudioControlAidl, DuckChangeExercise) { ASSERT_TRUE(audioControl->onDevicesToDuckChange(duckingInfos).isOk()); } +TEST_P(AudioControlAidl, FocusChangeWithMetaDataExercise) { + ALOGI("Focus Change test"); + + audiohalcommon::PlaybackTrackMetadata metadata; + metadata.usage = audiomediacommon::AudioUsage::MEDIA; + metadata.contentType = audiomediacommon::AudioContentType::MUSIC; + metadata.tags = {"com.google.android=VR"}; + ASSERT_TRUE( + audioControl + ->onAudioFocusChangeWithMetaData(metadata, 0, AudioFocusChange::GAIN_TRANSIENT) + .isOk()); +}; + +TEST_P(AudioControlAidl, SetAudioDeviceGainsChangedExercise) { + ALOGI("Set Audio Gains Changed test"); + + const std::vector<Reasons> reasons{Reasons::FORCED_MASTER_MUTE, Reasons::NAV_DUCKING}; + AudioGainConfigInfo agci1; + agci1.zoneId = 0; + agci1.devicePortAddress = String16("address 1"); + agci1.volumeIndex = 8; + + AudioGainConfigInfo agci2; + agci1.zoneId = 0; + agci1.devicePortAddress = String16("address 2"); + agci1.volumeIndex = 1; + + std::vector<AudioGainConfigInfo> gains{agci1, agci2}; + ASSERT_TRUE(audioControl->setAudioDeviceGainsChanged(reasons, gains).isOk()); +} + +/* + * Test Audio Gain Callback registration. + * + * Verifies that: + * - registerGainCallback succeeds; + * - registering a second callback succeeds in replacing the first; + * - closing handle does not crash; + */ +struct AudioGainCallbackMock : BnAudioGainCallback { + MOCK_METHOD(Status, onAudioDeviceGainsChanged, + (const std::vector<Reasons>& reasons, + const std::vector<AudioGainConfigInfo>& gains)); +}; + +TEST_P(AudioControlAidl, AudioGainCallbackRegistration) { + ALOGI("Focus listener test"); + + sp<AudioGainCallbackMock> gainCallback = new AudioGainCallbackMock(); + ASSERT_TRUE(audioControl->registerGainCallback(gainCallback).isOk()); + + sp<AudioGainCallbackMock> gainCallback2 = new AudioGainCallbackMock(); + ASSERT_TRUE(audioControl->registerGainCallback(gainCallback2).isOk()); +} + GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(AudioControlAidl); INSTANTIATE_TEST_SUITE_P( Audiocontrol, AudioControlAidl, diff --git a/automotive/vehicle/aidl/android/hardware/automotive/vehicle/IVehicle.aidl b/automotive/vehicle/aidl/android/hardware/automotive/vehicle/IVehicle.aidl index dc9b876f60..47fc54b8dc 100644 --- a/automotive/vehicle/aidl/android/hardware/automotive/vehicle/IVehicle.aidl +++ b/automotive/vehicle/aidl/android/hardware/automotive/vehicle/IVehicle.aidl @@ -90,6 +90,14 @@ interface IVehicle { * area ID) are not allowed in a single call. This function must return * {@link StatusCode#INVALID_ARG} for duplicate properties. * + * The {@link VehiclePropValue#timestamp} field in request is ignored. The + * {@link VehiclePropValue#timestamp} field in {@link GetValueResult} must + * be the system uptime since boot when the value changes for + * ON_CHANGE property or when the value is checked according to polling rate + * for CONTINUOUS property. Note that for CONTINUOUS property, VHAL client + * reading the property multiple times between the polling interval will get + * the same timestamp. + * * @param callback A callback interface, whose 'onGetValues' would be called * after the value is fetched. Caller should use * {@code android-automotive-large-parcelable} library to parse the @@ -104,7 +112,7 @@ interface IVehicle { * Set vehicle property values. * * The {@link IVehicleCallback#onSetValues} function would be called after - * the values set request are sent through vehicle bus or are failed to set. + * the values set request are sent through vehicle bus or failed to set. * If the bus protocol supports confirmation, the callback would be called * after getting the confirmation. * @@ -152,11 +160,36 @@ interface IVehicle { * Clients must be able to subscribe to multiple properties at a time * depending on data provided in options argument. * - * For one callback, the is only one subscription for one property. + * For one callback, there is only one subscription for one property. * A new subscription with a different sample rate would override the old * subscription. One property could be subscribed multiple times for * different callbacks. * + * If error is returned, some of the properties failed to subscribe. + * Caller is safe to try again, since subscribing to an already subscribed + * property is okay. + * + * The specified sample rate is just a guidance. It is not guaranteed that + * the sample rate is achievable depending on how the polling refresh rate + * is. The actual property event rate might be higher/lower than the + * specified sampleRate, for example, if the polling rate can be 5 times/s + * or 10 times/s, subscribing to a sample rate of 7 might use the 5 times/s + * polling rate, thus generating 5 events/s. We only require that on + * average, the {@code minSampleRate} and {@code maxSampleRate} can be + * achieved, all the sampleRate within min and max would on average + * generates events with rate >= {@code minSampleRate} and <= + * {@code maxSampleRate}. + * + * The {@link VehiclePropValue#timestamp} field for each property event must + * be the system uptime since boot when the value changes for + * ON_CHANGE property or when the value is checked according to polling rate + * for CONTINUOUS property. Note that for CONTINUOUS property, VHAL client + * reading the property multiple times between the polling interval will get + * the same timestamp. + * For example, if the polling rate for a property is 10 times/s, no matter + * what the sampleRate specified in {@code options}, the timestamp for + * the timestamp is updated 10 times/s. + * * @param callback The subscription callbacks. * {@link IVehicleCallback#onPropertyEvent} would be called when a new * property event arrives. @@ -189,8 +222,13 @@ interface IVehicle { /** * Unsubscribes from property events. * - * If 'callback' is not valid or 'propIds' were not subscribed for this - * 'callback', this method must return {@link StatusCode#INVALID_ARG}. + * If 'callback' is not valid this method must return + * {@link StatusCode#INVALID_ARG}. If a specified propId was not subscribed + * before, this method must ignore that propId. + * + * If error is returned, some of the properties failed to unsubscribe. + * Caller is safe to try again, since unsubscribing an already unsubscribed + * property is okay. * * @param callback The callback used in the previous subscription. * @param propIds The IDs for the properties to unsubscribe. diff --git a/automotive/vehicle/aidl/impl/fake_impl/hardware/include/FakeVehicleHardware.h b/automotive/vehicle/aidl/impl/fake_impl/hardware/include/FakeVehicleHardware.h index e799a28944..34b2b2454f 100644 --- a/automotive/vehicle/aidl/impl/fake_impl/hardware/include/FakeVehicleHardware.h +++ b/automotive/vehicle/aidl/impl/fake_impl/hardware/include/FakeVehicleHardware.h @@ -17,10 +17,12 @@ #ifndef android_hardware_automotive_vehicle_aidl_impl_fake_impl_hardware_include_FakeVehicleHardware_H_ #define android_hardware_automotive_vehicle_aidl_impl_fake_impl_hardware_include_FakeVehicleHardware_H_ +#include <ConcurrentQueue.h> #include <DefaultConfig.h> #include <FakeObd2Frame.h> #include <FakeUserHal.h> #include <IVehicleHardware.h> +#include <RecurrentTimer.h> #include <VehicleHalTypes.h> #include <VehiclePropertyStore.h> #include <android-base/parseint.h> @@ -47,6 +49,8 @@ class FakeVehicleHardware : public IVehicleHardware { explicit FakeVehicleHardware(std::unique_ptr<VehiclePropValuePool> valuePool); + ~FakeVehicleHardware(); + // Get all the property configs. std::vector<aidl::android::hardware::automotive::vehicle::VehiclePropConfig> getAllPropertyConfigs() const override; @@ -82,6 +86,10 @@ class FakeVehicleHardware : public IVehicleHardware { void registerOnPropertySetErrorEvent( std::unique_ptr<const PropertySetErrorCallback> callback) override; + // Update the sample rate for the [propId, areaId] pair. + aidl::android::hardware::automotive::vehicle::StatusCode updateSampleRate( + int32_t propId, int32_t areaId, float sampleRate) override; + protected: // mValuePool is also used in mServerSidePropStore. const std::shared_ptr<VehiclePropValuePool> mValuePool; @@ -97,13 +105,45 @@ class FakeVehicleHardware : public IVehicleHardware { // Expose private methods to unit test. friend class FakeVehicleHardwareTestHelper; + template <class CallbackType, class RequestType> + struct RequestWithCallback { + RequestType request; + std::shared_ptr<const CallbackType> callback; + }; + + template <class CallbackType, class RequestType> + class PendingRequestHandler { + public: + PendingRequestHandler(FakeVehicleHardware* hardware); + + void addRequest(RequestType request, std::shared_ptr<const CallbackType> callback); + + void stop(); + + private: + FakeVehicleHardware* mHardware; + std::thread mThread; + ConcurrentQueue<RequestWithCallback<CallbackType, RequestType>> mRequests; + + void handleRequestsOnce(); + }; + const std::unique_ptr<obd2frame::FakeObd2Frame> mFakeObd2Frame; const std::unique_ptr<FakeUserHal> mFakeUserHal; - std::mutex mCallbackLock; - std::unique_ptr<const PropertyChangeCallback> mOnPropertyChangeCallback - GUARDED_BY(mCallbackLock); - std::unique_ptr<const PropertySetErrorCallback> mOnPropertySetErrorCallback - GUARDED_BY(mCallbackLock); + // RecurrentTimer is thread-safe. + std::unique_ptr<RecurrentTimer> mRecurrentTimer; + std::mutex mLock; + std::unique_ptr<const PropertyChangeCallback> mOnPropertyChangeCallback GUARDED_BY(mLock); + std::unique_ptr<const PropertySetErrorCallback> mOnPropertySetErrorCallback GUARDED_BY(mLock); + std::unordered_map<PropIdAreaId, std::shared_ptr<RecurrentTimer::Callback>, PropIdAreaIdHash> + mRecurrentActions GUARDED_BY(mLock); + // PendingRequestHandler is thread-safe. + mutable PendingRequestHandler<GetValuesCallback, + aidl::android::hardware::automotive::vehicle::GetValueRequest> + mPendingGetValueRequests; + mutable PendingRequestHandler<SetValuesCallback, + aidl::android::hardware::automotive::vehicle::SetValueRequest> + mPendingSetValueRequests; void init(); // Stores the initial value to property store. @@ -163,6 +203,10 @@ class FakeVehicleHardware : public IVehicleHardware { android::base::Result<void> checkArgumentsSize(const std::vector<std::string>& options, size_t minSize); + aidl::android::hardware::automotive::vehicle::GetValueResult handleGetValueRequest( + const aidl::android::hardware::automotive::vehicle::GetValueRequest& request); + aidl::android::hardware::automotive::vehicle::SetValueResult handleSetValueRequest( + const aidl::android::hardware::automotive::vehicle::SetValueRequest& request); }; } // namespace fake diff --git a/automotive/vehicle/aidl/impl/fake_impl/hardware/src/FakeVehicleHardware.cpp b/automotive/vehicle/aidl/impl/fake_impl/hardware/src/FakeVehicleHardware.cpp index f8b64f2a0a..7b3de586c8 100644 --- a/automotive/vehicle/aidl/impl/fake_impl/hardware/src/FakeVehicleHardware.cpp +++ b/automotive/vehicle/aidl/impl/fake_impl/hardware/src/FakeVehicleHardware.cpp @@ -66,6 +66,7 @@ using ::android::base::EqualsIgnoreCase; using ::android::base::Error; using ::android::base::ParseFloat; using ::android::base::Result; +using ::android::base::ScopedLockAssertion; using ::android::base::StartsWith; using ::android::base::StringPrintf; @@ -131,21 +132,24 @@ void FakeVehicleHardware::storePropInitialValue(const defaultconfig::ConfigDecla } FakeVehicleHardware::FakeVehicleHardware() - : mValuePool(new VehiclePropValuePool), - mServerSidePropStore(new VehiclePropertyStore(mValuePool)), - mFakeObd2Frame(new obd2frame::FakeObd2Frame(mServerSidePropStore)), - mFakeUserHal(new FakeUserHal(mValuePool)) { - init(); -} + : FakeVehicleHardware(std::make_unique<VehiclePropValuePool>()) {} FakeVehicleHardware::FakeVehicleHardware(std::unique_ptr<VehiclePropValuePool> valuePool) : mValuePool(std::move(valuePool)), mServerSidePropStore(new VehiclePropertyStore(mValuePool)), mFakeObd2Frame(new obd2frame::FakeObd2Frame(mServerSidePropStore)), - mFakeUserHal(new FakeUserHal(mValuePool)) { + mFakeUserHal(new FakeUserHal(mValuePool)), + mRecurrentTimer(new RecurrentTimer()), + mPendingGetValueRequests(this), + mPendingSetValueRequests(this) { init(); } +FakeVehicleHardware::~FakeVehicleHardware() { + mPendingGetValueRequests.stop(); + mPendingSetValueRequests.stop(); +} + void FakeVehicleHardware::init() { for (auto& it : defaultconfig::getDefaultConfigs()) { VehiclePropConfig cfg = it.config; @@ -430,37 +434,25 @@ VhalResult<void> FakeVehicleHardware::maybeSetSpecialValue(const VehiclePropValu StatusCode FakeVehicleHardware::setValues(std::shared_ptr<const SetValuesCallback> callback, const std::vector<SetValueRequest>& requests) { - std::vector<SetValueResult> results; for (auto& request : requests) { - const VehiclePropValue& value = request.value; - int propId = value.prop; - if (FAKE_VEHICLEHARDWARE_DEBUG) { - ALOGD("Set value for property ID: %d", propId); + ALOGD("Set value for property ID: %d", request.value.prop); } - SetValueResult setValueResult; - setValueResult.requestId = request.requestId; - - if (auto result = setValue(value); !result.ok()) { - ALOGE("failed to set value, error: %s, code: %d", getErrorMsg(result).c_str(), - getIntErrorCode(result)); - setValueResult.status = getErrorCode(result); - } else { - setValueResult.status = StatusCode::OK; - } - - results.push_back(std::move(setValueResult)); + // In a real VHAL implementation, you could either send the setValue request to vehicle bus + // here in the binder thread, or you could send the request in setValue which runs in + // the handler thread. If you decide to send the setValue request here, you should not + // wait for the response here and the handler thread should handle the setValue response. + mPendingSetValueRequests.addRequest(request, callback); } - // In the real vhal, the values will be sent to Car ECU. We just pretend it is done here and - // send back the updated property values to client. - (*callback)(std::move(results)); - return StatusCode::OK; } VhalResult<void> FakeVehicleHardware::setValue(const VehiclePropValue& value) { + // In a real VHAL implementation, this will send the request to vehicle bus if not already + // sent in setValues, and wait for the response from vehicle bus. + // Here we are just updating mValuePool. bool isSpecialValue = false; auto setSpecialValueResult = maybeSetSpecialValue(value, &isSpecialValue); @@ -487,41 +479,59 @@ VhalResult<void> FakeVehicleHardware::setValue(const VehiclePropValue& value) { return {}; } +SetValueResult FakeVehicleHardware::handleSetValueRequest(const SetValueRequest& request) { + SetValueResult setValueResult; + setValueResult.requestId = request.requestId; + + if (auto result = setValue(request.value); !result.ok()) { + ALOGE("failed to set value, error: %s, code: %d", getErrorMsg(result).c_str(), + getIntErrorCode(result)); + setValueResult.status = getErrorCode(result); + } else { + setValueResult.status = StatusCode::OK; + } + + return setValueResult; +} + StatusCode FakeVehicleHardware::getValues(std::shared_ptr<const GetValuesCallback> callback, const std::vector<GetValueRequest>& requests) const { - std::vector<GetValueResult> results; for (auto& request : requests) { - const VehiclePropValue& value = request.prop; - if (FAKE_VEHICLEHARDWARE_DEBUG) { - ALOGD("getValues(%d)", value.prop); + ALOGD("getValues(%d)", request.prop.prop); } - GetValueResult getValueResult; - getValueResult.requestId = request.requestId; - - auto result = getValue(value); - if (!result.ok()) { - ALOGE("failed to get value, error: %s, code: %d", getErrorMsg(result).c_str(), - getIntErrorCode(result)); - getValueResult.status = getErrorCode(result); - } else { - getValueResult.status = StatusCode::OK; - getValueResult.prop = *result.value(); - } - results.push_back(std::move(getValueResult)); + // In a real VHAL implementation, you could either send the getValue request to vehicle bus + // here in the binder thread, or you could send the request in getValue which runs in + // the handler thread. If you decide to send the getValue request here, you should not + // wait for the response here and the handler thread should handle the getValue response. + mPendingGetValueRequests.addRequest(request, callback); } - // In a real VHAL implementation, getValue would be async and we would call the callback after - // we actually received the values from vehicle bus. Here we are getting the result - // synchronously so we could call the callback here. - (*callback)(std::move(results)); - return StatusCode::OK; } +GetValueResult FakeVehicleHardware::handleGetValueRequest(const GetValueRequest& request) { + GetValueResult getValueResult; + getValueResult.requestId = request.requestId; + + auto result = getValue(request.prop); + if (!result.ok()) { + ALOGE("failed to get value, error: %s, code: %d", getErrorMsg(result).c_str(), + getIntErrorCode(result)); + getValueResult.status = getErrorCode(result); + } else { + getValueResult.status = StatusCode::OK; + getValueResult.prop = *result.value(); + } + return getValueResult; +} + FakeVehicleHardware::ValueResultType FakeVehicleHardware::getValue( const VehiclePropValue& value) const { + // In a real VHAL implementation, this will send the request to vehicle bus if not already + // sent in getValues, and wait for the response from vehicle bus. + // Here we are just reading value from mValuePool. bool isSpecialValue = false; auto result = maybeGetSpecialValue(value, &isSpecialValue); if (isSpecialValue) { @@ -567,6 +577,12 @@ DumpResult FakeVehicleHardware::dump(const std::vector<std::string>& options) { result.buffer = dumpSpecificProperty(options); } else if (EqualsIgnoreCase(option, "--set")) { result.buffer = dumpSetProperties(options); + } else if (EqualsIgnoreCase(option, kUserHalDumpOption)) { + if (options.size() == 1) { + result.buffer = mFakeUserHal->showDumpHelp(); + } else { + result.buffer = mFakeUserHal->dump(options[1]); + } } else { result.buffer = StringPrintf("Invalid option: %s\n", option.c_str()); } @@ -584,7 +600,9 @@ std::string FakeVehicleHardware::dumpHelp() { "[-b BYTES_VALUE] [-a AREA_ID] : sets the value of property PROP. " "Notice that the string, bytes and area value can be set just once, while the other can" " have multiple values (so they're used in the respective array), " - "BYTES_VALUE is in the form of 0xXXXX, e.g. 0xdeadbeef.\n"; + "BYTES_VALUE is in the form of 0xXXXX, e.g. 0xdeadbeef.\n\n" + "Fake user HAL usage: \n" + + mFakeUserHal->showDumpHelp(); } std::string FakeVehicleHardware::dumpAllProperties() { @@ -837,18 +855,57 @@ StatusCode FakeVehicleHardware::checkHealth() { void FakeVehicleHardware::registerOnPropertyChangeEvent( std::unique_ptr<const PropertyChangeCallback> callback) { - std::scoped_lock<std::mutex> lockGuard(mCallbackLock); + std::scoped_lock<std::mutex> lockGuard(mLock); mOnPropertyChangeCallback = std::move(callback); } void FakeVehicleHardware::registerOnPropertySetErrorEvent( std::unique_ptr<const PropertySetErrorCallback> callback) { - std::scoped_lock<std::mutex> lockGuard(mCallbackLock); + std::scoped_lock<std::mutex> lockGuard(mLock); mOnPropertySetErrorCallback = std::move(callback); } +StatusCode FakeVehicleHardware::updateSampleRate(int32_t propId, int32_t areaId, float sampleRate) { + // DefaultVehicleHal makes sure that sampleRate must be within minSampleRate and maxSampleRate. + // For fake implementation, we would write the same value with a new timestamp into propStore + // at sample rate. + std::scoped_lock<std::mutex> lockGuard(mLock); + + PropIdAreaId propIdAreaId{ + .propId = propId, + .areaId = areaId, + }; + if (mRecurrentActions.find(propIdAreaId) != mRecurrentActions.end()) { + mRecurrentTimer->unregisterTimerCallback(mRecurrentActions[propIdAreaId]); + } + if (sampleRate == 0) { + return StatusCode::OK; + } + int64_t interval = static_cast<int64_t>(1'000'000'000. / sampleRate); + auto action = std::make_shared<RecurrentTimer::Callback>([this, propId, areaId] { + // Refresh the property value. In real implementation, this should poll the latest value + // from vehicle bus. Here, we are just refreshing the existing value with a new timestamp. + auto result = getValue(VehiclePropValue{ + .prop = propId, + .areaId = areaId, + }); + if (!result.ok()) { + // Failed to read current value, skip refreshing. + return; + } + result.value()->timestamp = elapsedRealtimeNano(); + // Must remove the value before writing, otherwise, we would generate no update event since + // the value is the same. + mServerSidePropStore->removeValue(*result.value()); + mServerSidePropStore->writeValue(std::move(result.value())); + }); + mRecurrentTimer->registerTimerCallback(interval, action); + mRecurrentActions[propIdAreaId] = action; + return StatusCode::OK; +} + void FakeVehicleHardware::onValueChangeCallback(const VehiclePropValue& value) { - std::scoped_lock<std::mutex> lockGuard(mCallbackLock); + std::scoped_lock<std::mutex> lockGuard(mLock); if (mOnPropertyChangeCallback == nullptr) { return; @@ -935,6 +992,60 @@ Result<std::vector<uint8_t>> FakeVehicleHardware::parseHexString(const std::stri return bytes; } +template <class CallbackType, class RequestType> +FakeVehicleHardware::PendingRequestHandler<CallbackType, RequestType>::PendingRequestHandler( + FakeVehicleHardware* hardware) + : mHardware(hardware), mThread([this] { + while (mRequests.waitForItems()) { + handleRequestsOnce(); + } + }) {} + +template <class CallbackType, class RequestType> +void FakeVehicleHardware::PendingRequestHandler<CallbackType, RequestType>::addRequest( + RequestType request, std::shared_ptr<const CallbackType> callback) { + mRequests.push({ + request, + callback, + }); +} + +template <class CallbackType, class RequestType> +void FakeVehicleHardware::PendingRequestHandler<CallbackType, RequestType>::stop() { + mRequests.deactivate(); + if (mThread.joinable()) { + mThread.join(); + } +} + +template <> +void FakeVehicleHardware::PendingRequestHandler<FakeVehicleHardware::GetValuesCallback, + GetValueRequest>::handleRequestsOnce() { + std::unordered_map<std::shared_ptr<const GetValuesCallback>, std::vector<GetValueResult>> + callbackToResults; + for (const auto& rwc : mRequests.flush()) { + auto result = mHardware->handleGetValueRequest(rwc.request); + callbackToResults[rwc.callback].push_back(std::move(result)); + } + for (const auto& [callback, results] : callbackToResults) { + (*callback)(std::move(results)); + } +} + +template <> +void FakeVehicleHardware::PendingRequestHandler<FakeVehicleHardware::SetValuesCallback, + SetValueRequest>::handleRequestsOnce() { + std::unordered_map<std::shared_ptr<const SetValuesCallback>, std::vector<SetValueResult>> + callbackToResults; + for (const auto& rwc : mRequests.flush()) { + auto result = mHardware->handleSetValueRequest(rwc.request); + callbackToResults[rwc.callback].push_back(std::move(result)); + } + for (const auto& [callback, results] : callbackToResults) { + (*callback)(std::move(results)); + } +} + } // namespace fake } // namespace vehicle } // namespace automotive diff --git a/automotive/vehicle/aidl/impl/fake_impl/hardware/test/FakeVehicleHardwareTest.cpp b/automotive/vehicle/aidl/impl/fake_impl/hardware/test/FakeVehicleHardwareTest.cpp index 7a7fb3745a..3e8f634ab7 100644 --- a/automotive/vehicle/aidl/impl/fake_impl/hardware/test/FakeVehicleHardwareTest.cpp +++ b/automotive/vehicle/aidl/impl/fake_impl/hardware/test/FakeVehicleHardwareTest.cpp @@ -25,12 +25,17 @@ #include <android-base/expected.h> #include <android-base/file.h> #include <android-base/stringprintf.h> +#include <android-base/thread_annotations.h> #include <gmock/gmock.h> #include <gtest/gtest.h> #include <utils/Log.h> #include <utils/SystemClock.h> #include <inttypes.h> +#include <chrono> +#include <condition_variable> +#include <unordered_map> +#include <unordered_set> #include <vector> namespace android { @@ -53,6 +58,7 @@ using ::aidl::android::hardware::automotive::vehicle::VehicleProperty; using ::aidl::android::hardware::automotive::vehicle::VehiclePropertyStatus; using ::aidl::android::hardware::automotive::vehicle::VehiclePropValue; using ::android::base::expected; +using ::android::base::ScopedLockAssertion; using ::android::base::StringPrintf; using ::android::base::unexpected; using ::testing::ContainerEq; @@ -60,6 +66,8 @@ using ::testing::ContainsRegex; using ::testing::Eq; using ::testing::WhenSortedBy; +using std::chrono::milliseconds; + constexpr int INVALID_PROP_ID = 0; constexpr char CAR_MAKE[] = "Default Car"; @@ -93,11 +101,51 @@ class FakeVehicleHardwareTest : public ::testing::Test { FakeVehicleHardware* getHardware() { return &mHardware; } StatusCode setValues(const std::vector<SetValueRequest>& requests) { - return getHardware()->setValues(mSetValuesCallback, requests); + { + std::scoped_lock<std::mutex> lockGuard(mLock); + for (const auto& request : requests) { + mPendingSetValueRequests.insert(request.requestId); + } + } + if (StatusCode status = getHardware()->setValues(mSetValuesCallback, requests); + status != StatusCode::OK) { + return status; + } + std::unique_lock<std::mutex> lk(mLock); + // Wait for the onSetValueResults. + bool result = mCv.wait_for(lk, milliseconds(1000), [this] { + ScopedLockAssertion lockAssertion(mLock); + return mPendingSetValueRequests.size() == 0; + }); + if (!result) { + ALOGE("wait for callbacks for setValues timed-out"); + return StatusCode::INTERNAL_ERROR; + } + return StatusCode::OK; } StatusCode getValues(const std::vector<GetValueRequest>& requests) { - return getHardware()->getValues(mGetValuesCallback, requests); + { + std::scoped_lock<std::mutex> lockGuard(mLock); + for (const auto& request : requests) { + mPendingGetValueRequests.insert(request.requestId); + } + } + if (StatusCode status = getHardware()->getValues(mGetValuesCallback, requests); + status != StatusCode::OK) { + return status; + } + std::unique_lock<std::mutex> lk(mLock); + // Wait for the onGetValueResults. + bool result = mCv.wait_for(lk, milliseconds(1000), [this] { + ScopedLockAssertion lockAssertion(mLock); + return mPendingGetValueRequests.size() == 0; + }); + if (!result) { + ALOGE("wait for callbacks for getValues timed-out"); + return StatusCode::INTERNAL_ERROR; + } + return StatusCode::OK; } StatusCode setValue(const VehiclePropValue& value) { @@ -158,30 +206,69 @@ class FakeVehicleHardwareTest : public ::testing::Test { } void onSetValues(std::vector<SetValueResult> results) { + std::scoped_lock<std::mutex> lockGuard(mLock); for (auto& result : results) { mSetValueResults.push_back(result); + mPendingSetValueRequests.erase(result.requestId); } + mCv.notify_all(); } - const std::vector<SetValueResult>& getSetValueResults() { return mSetValueResults; } + const std::vector<SetValueResult>& getSetValueResults() { + std::scoped_lock<std::mutex> lockGuard(mLock); + return mSetValueResults; + } void onGetValues(std::vector<GetValueResult> results) { + std::scoped_lock<std::mutex> lockGuard(mLock); for (auto& result : results) { mGetValueResults.push_back(result); + mPendingGetValueRequests.erase(result.requestId); } + mCv.notify_all(); } - const std::vector<GetValueResult>& getGetValueResults() { return mGetValueResults; } + const std::vector<GetValueResult>& getGetValueResults() { + std::scoped_lock<std::mutex> lockGuard(mLock); + return mGetValueResults; + } void onPropertyChangeEvent(std::vector<VehiclePropValue> values) { + std::scoped_lock<std::mutex> lockGuard(mLock); for (auto& value : values) { mChangedProperties.push_back(value); + PropIdAreaId propIdAreaId{ + .propId = value.prop, + .areaId = value.areaId, + }; + mEventCount[propIdAreaId]++; } + mCv.notify_all(); } - const std::vector<VehiclePropValue>& getChangedProperties() { return mChangedProperties; } + const std::vector<VehiclePropValue>& getChangedProperties() { + std::scoped_lock<std::mutex> lockGuard(mLock); + return mChangedProperties; + } - void clearChangedProperties() { mChangedProperties.clear(); } + bool waitForChangedProperties(int32_t propId, int32_t areaId, size_t count, + milliseconds timeout) { + PropIdAreaId propIdAreaId{ + .propId = propId, + .areaId = areaId, + }; + std::unique_lock<std::mutex> lk(mLock); + return mCv.wait_for(lk, timeout, [this, propIdAreaId, count] { + ScopedLockAssertion lockAssertion(mLock); + return mEventCount[propIdAreaId] >= count; + }); + } + + void clearChangedProperties() { + std::scoped_lock<std::mutex> lockGuard(mLock); + mEventCount.clear(); + mChangedProperties.clear(); + } static void addSetValueRequest(std::vector<SetValueRequest>& requests, std::vector<SetValueResult>& expectedResults, int64_t requestId, @@ -246,11 +333,16 @@ class FakeVehicleHardwareTest : public ::testing::Test { private: FakeVehicleHardware mHardware; - std::vector<SetValueResult> mSetValueResults; - std::vector<GetValueResult> mGetValueResults; - std::vector<VehiclePropValue> mChangedProperties; std::shared_ptr<IVehicleHardware::SetValuesCallback> mSetValuesCallback; std::shared_ptr<IVehicleHardware::GetValuesCallback> mGetValuesCallback; + std::condition_variable mCv; + std::mutex mLock; + std::unordered_map<PropIdAreaId, size_t, PropIdAreaIdHash> mEventCount GUARDED_BY(mLock); + std::vector<SetValueResult> mSetValueResults GUARDED_BY(mLock); + std::vector<GetValueResult> mGetValueResults GUARDED_BY(mLock); + std::vector<VehiclePropValue> mChangedProperties GUARDED_BY(mLock); + std::unordered_set<int64_t> mPendingSetValueRequests GUARDED_BY(mLock); + std::unordered_set<int64_t> mPendingGetValueRequests GUARDED_BY(mLock); }; TEST_F(FakeVehicleHardwareTest, testGetAllPropertyConfigs) { @@ -1313,6 +1405,28 @@ TEST_F(FakeVehicleHardwareTest, testDumpInvalidOptions) { ASSERT_THAT(result.buffer, ContainsRegex("Invalid option: --invalid")); } +TEST_F(FakeVehicleHardwareTest, testDumpFakeUserHalHelp) { + std::vector<std::string> options; + options.push_back("--user-hal"); + + DumpResult result = getHardware()->dump(options); + ASSERT_FALSE(result.callerShouldDumpState); + ASSERT_NE(result.buffer, ""); + ASSERT_THAT(result.buffer, ContainsRegex("dumps state used for user management")); +} + +TEST_F(FakeVehicleHardwareTest, testDumpFakeUserHal) { + std::vector<std::string> options; + options.push_back("--user-hal"); + // Indent: " ". + options.push_back(" "); + + DumpResult result = getHardware()->dump(options); + ASSERT_FALSE(result.callerShouldDumpState); + ASSERT_NE(result.buffer, ""); + ASSERT_THAT(result.buffer, ContainsRegex(" No InitialUserInfo response\n")); +} + struct SetPropTestCase { std::string test_name; std::vector<std::string> options; @@ -1510,6 +1624,35 @@ TEST_F(FakeVehicleHardwareTest, testGetEchoReverseBytes) { ASSERT_EQ(result.value().value.byteValues, std::vector<uint8_t>({0x04, 0x03, 0x02, 0x01})); } +TEST_F(FakeVehicleHardwareTest, testUpdateSampleRate) { + int32_t propSpeed = toInt(VehicleProperty::PERF_VEHICLE_SPEED); + int32_t propSteering = toInt(VehicleProperty::PERF_STEERING_ANGLE); + int32_t areaId = 0; + getHardware()->updateSampleRate(propSpeed, areaId, 5); + + ASSERT_TRUE(waitForChangedProperties(propSpeed, areaId, /*count=*/5, milliseconds(1500))) + << "not enough events generated for speed"; + + getHardware()->updateSampleRate(propSteering, areaId, 10); + + ASSERT_TRUE(waitForChangedProperties(propSteering, areaId, /*count=*/10, milliseconds(1500))) + << "not enough events generated for steering"; + + int64_t timestamp = elapsedRealtimeNano(); + // Disable refreshing for propSpeed. + getHardware()->updateSampleRate(propSpeed, areaId, 0); + clearChangedProperties(); + + ASSERT_TRUE(waitForChangedProperties(propSteering, areaId, /*count=*/5, milliseconds(1500))) + << "should still receive steering events after disable polling for speed"; + auto updatedValues = getChangedProperties(); + for (auto& value : updatedValues) { + ASSERT_GE(value.timestamp, timestamp); + ASSERT_EQ(value.prop, propSteering); + ASSERT_EQ(value.areaId, areaId); + } +} + } // namespace fake } // namespace vehicle } // namespace automotive diff --git a/automotive/vehicle/aidl/impl/hardware/include/IVehicleHardware.h b/automotive/vehicle/aidl/impl/hardware/include/IVehicleHardware.h index 4a38827574..759db4153d 100644 --- a/automotive/vehicle/aidl/impl/hardware/include/IVehicleHardware.h +++ b/automotive/vehicle/aidl/impl/hardware/include/IVehicleHardware.h @@ -80,6 +80,35 @@ class IVehicleHardware { const std::vector<aidl::android::hardware::automotive::vehicle::GetValueRequest>& requests) const = 0; + // Update the sampling rate for the specified property and the specified areaId (0 for global + // property) if server supports it. The property must be a continuous property. + // {@code sampleRate} means that for this specific property, the server must generate at least + // this many OnPropertyChange events per seconds. + // A sampleRate of 0 means the property is no longer subscribed and server does not need to + // generate any onPropertyEvent for this property. + // This would be called if sample rate is updated for a subscriber, a new subscriber is added + // or an existing subscriber is removed. For example: + // 1. We have no subscriber for speed. + // 2. A new subscriber is subscribing speed for 10 times/s, updsateSampleRate would be called + // with sampleRate as 10. The impl is now polling vehicle speed from bus 10 times/s. + // 3. A new subscriber is subscribing speed for 5 times/s, because it is less than 10 + // times/sec, updateSampleRate would not be called. + // 4. The initial subscriber is removed, updateSampleRate would be called with sampleRate as + // 5, because now it only needs to report event 5times/sec. The impl can now poll vehicle + // speed 5 times/s. If the impl is still polling at 10 times/s, that is okay as long as + // the polling rate is larger than 5times/s. DefaultVehicleHal would ignore the additional + // events. + // 5. The second subscriber is removed, updateSampleRate would be called with sampleRate as 0. + // The impl can optionally disable the polling for vehicle speed. + // + // If the impl is always polling at {@code maxSampleRate} as specified in config, then this + // function can be a no-op. + virtual aidl::android::hardware::automotive::vehicle::StatusCode updateSampleRate( + [[maybe_unused]] int32_t propId, [[maybe_unused]] int32_t areaId, + [[maybe_unused]] float sampleRate) { + return aidl::android::hardware::automotive::vehicle::StatusCode::OK; + } + // Dump debug information in the server. virtual DumpResult dump(const std::vector<std::string>& options) = 0; diff --git a/automotive/vehicle/aidl/impl/vhal/include/RecurrentTimer.h b/automotive/vehicle/aidl/impl/utils/common/include/RecurrentTimer.h index 5f0f7161c2..5f0f7161c2 100644 --- a/automotive/vehicle/aidl/impl/vhal/include/RecurrentTimer.h +++ b/automotive/vehicle/aidl/impl/utils/common/include/RecurrentTimer.h diff --git a/automotive/vehicle/aidl/impl/utils/common/include/VehicleUtils.h b/automotive/vehicle/aidl/impl/utils/common/include/VehicleUtils.h index 6d7d13120d..c94bad6ca6 100644 --- a/automotive/vehicle/aidl/impl/utils/common/include/VehicleUtils.h +++ b/automotive/vehicle/aidl/impl/utils/common/include/VehicleUtils.h @@ -21,6 +21,7 @@ #include <android-base/format.h> #include <android-base/result.h> +#include <math/HashCombine.h> #include <utils/Log.h> namespace android { @@ -310,6 +311,24 @@ ndk::ScopedAStatus toScopedAStatus(const VhalResult<T>& result, return toScopedAStatus(result, getErrorCode(result), additionalErrorMsg); } +struct PropIdAreaId { + int32_t propId; + int32_t areaId; + + inline bool operator==(const PropIdAreaId& other) const { + return areaId == other.areaId && propId == other.propId; + } +}; + +struct PropIdAreaIdHash { + inline size_t operator()(const PropIdAreaId& propIdAreaId) const { + size_t res = 0; + hashCombine(res, propIdAreaId.propId); + hashCombine(res, propIdAreaId.areaId); + return res; + } +}; + } // namespace vehicle } // namespace automotive } // namespace hardware diff --git a/automotive/vehicle/aidl/impl/vhal/src/RecurrentTimer.cpp b/automotive/vehicle/aidl/impl/utils/common/src/RecurrentTimer.cpp index 8521c4db7c..8521c4db7c 100644 --- a/automotive/vehicle/aidl/impl/vhal/src/RecurrentTimer.cpp +++ b/automotive/vehicle/aidl/impl/utils/common/src/RecurrentTimer.cpp diff --git a/automotive/vehicle/aidl/impl/vhal/test/RecurrentTimerTest.cpp b/automotive/vehicle/aidl/impl/utils/common/test/RecurrentTimerTest.cpp index a033a248cd..a033a248cd 100644 --- a/automotive/vehicle/aidl/impl/vhal/test/RecurrentTimerTest.cpp +++ b/automotive/vehicle/aidl/impl/utils/common/test/RecurrentTimerTest.cpp diff --git a/automotive/vehicle/aidl/impl/vhal/Android.bp b/automotive/vehicle/aidl/impl/vhal/Android.bp index 49f48f7aa8..5abcaf6980 100644 --- a/automotive/vehicle/aidl/impl/vhal/Android.bp +++ b/automotive/vehicle/aidl/impl/vhal/Android.bp @@ -54,7 +54,6 @@ cc_library { srcs: [ "src/ConnectedClient.cpp", "src/DefaultVehicleHal.cpp", - "src/RecurrentTimer.cpp", "src/SubscriptionManager.cpp", ], static_libs: [ diff --git a/automotive/vehicle/aidl/impl/vhal/include/DefaultVehicleHal.h b/automotive/vehicle/aidl/impl/vhal/include/DefaultVehicleHal.h index f646b6b1bb..9c29816faf 100644 --- a/automotive/vehicle/aidl/impl/vhal/include/DefaultVehicleHal.h +++ b/automotive/vehicle/aidl/impl/vhal/include/DefaultVehicleHal.h @@ -17,10 +17,11 @@ #ifndef android_hardware_automotive_vehicle_aidl_impl_vhal_include_DefaultVehicleHal_H_ #define android_hardware_automotive_vehicle_aidl_impl_vhal_include_DefaultVehicleHal_H_ -#include "ConnectedClient.h" -#include "ParcelableUtils.h" -#include "PendingRequestPool.h" -#include "SubscriptionManager.h" +#include <ConnectedClient.h> +#include <ParcelableUtils.h> +#include <PendingRequestPool.h> +#include <RecurrentTimer.h> +#include <SubscriptionManager.h> #include <ConcurrentQueue.h> #include <IVehicleHardware.h> @@ -163,7 +164,7 @@ class DefaultVehicleHal final : public aidl::android::hardware::automotive::vehi static constexpr int64_t TIMEOUT_IN_NANO = 30'000'000'000; // heart beat event interval: 3s static constexpr int64_t HEART_BEAT_INTERVAL_IN_NANO = 3'000'000'000; - const std::shared_ptr<IVehicleHardware> mVehicleHardware; + std::unique_ptr<IVehicleHardware> mVehicleHardware; // mConfigsByPropId and mConfigFile are only modified during initialization, so no need to // lock guard them. @@ -188,6 +189,8 @@ class DefaultVehicleHal final : public aidl::android::hardware::automotive::vehi // mBinderImpl is only going to be changed in test. std::unique_ptr<IBinder> mBinderImpl; + // Only initialized once. + std::shared_ptr<std::function<void()>> mRecurrentAction; // RecurrentTimer is thread-safe. RecurrentTimer mRecurrentTimer; @@ -243,18 +246,12 @@ class DefaultVehicleHal final : public aidl::android::hardware::automotive::vehi std::unordered_map<const AIBinder*, std::shared_ptr<T>>* clients, const CallbackType& callback, std::shared_ptr<PendingRequestPool> pendingRequestPool); - static void getValueFromHardwareCallCallback( - std::weak_ptr<IVehicleHardware> vehicleHardware, - std::shared_ptr<SubscribeIdByClient> subscribeIdByClient, - std::shared_ptr<SubscriptionClients> subscriptionClients, const CallbackType& callback, - const aidl::android::hardware::automotive::vehicle::VehiclePropValue& value); - static void onPropertyChangeEvent( std::weak_ptr<SubscriptionManager> subscriptionManager, const std::vector<aidl::android::hardware::automotive::vehicle::VehiclePropValue>& updatedValues); - static void checkHealth(std::weak_ptr<IVehicleHardware> hardware, + static void checkHealth(IVehicleHardware* hardware, std::weak_ptr<SubscriptionManager> subscriptionManager); static void onBinderDied(void* cookie); diff --git a/automotive/vehicle/aidl/impl/vhal/include/SubscriptionManager.h b/automotive/vehicle/aidl/impl/vhal/include/SubscriptionManager.h index b0d67010b1..7c8f1b4672 100644 --- a/automotive/vehicle/aidl/impl/vhal/include/SubscriptionManager.h +++ b/automotive/vehicle/aidl/impl/vhal/include/SubscriptionManager.h @@ -17,15 +17,16 @@ #ifndef android_hardware_automotive_vehicle_aidl_impl_vhal_include_SubscriptionManager_H_ #define android_hardware_automotive_vehicle_aidl_impl_vhal_include_SubscriptionManager_H_ -#include "RecurrentTimer.h" - +#include <IVehicleHardware.h> #include <VehicleHalTypes.h> +#include <VehicleUtils.h> #include <aidl/android/hardware/automotive/vehicle/IVehicleCallback.h> #include <android-base/result.h> #include <android-base/thread_annotations.h> #include <mutex> +#include <optional> #include <unordered_map> #include <unordered_set> #include <vector> @@ -35,43 +36,58 @@ namespace hardware { namespace automotive { namespace vehicle { +// A class to represent all the subscription configs for a continuous [propId, areaId]. +class ContSubConfigs final { + public: + using ClientIdType = const AIBinder*; + + void addClient(const ClientIdType& clientId, float sampleRate); + void removeClient(const ClientIdType& clientId); + float getMaxSampleRate(); + + private: + float mMaxSampleRate = 0.; + std::unordered_map<ClientIdType, float> mSampleRates; + + void refreshMaxSampleRate(); +}; + // A thread-safe subscription manager that manages all VHAL subscriptions. class SubscriptionManager final { public: using ClientIdType = const AIBinder*; using CallbackType = std::shared_ptr<aidl::android::hardware::automotive::vehicle::IVehicleCallback>; - using GetValueFunc = std::function<void( - const CallbackType& callback, - const aidl::android::hardware::automotive::vehicle::VehiclePropValue& value)>; - explicit SubscriptionManager(GetValueFunc&& action); + explicit SubscriptionManager(IVehicleHardware* hardware); ~SubscriptionManager(); // Subscribes to properties according to {@code SubscribeOptions}. Note that all option must // contain non-empty areaIds field, which contains all area IDs to subscribe. As a result, // the options here is different from the options passed from VHAL client. - // Returns error if any of the subscribe options is not valid. If error is returned, no - // properties would be subscribed. + // Returns error if any of the subscribe options is not valid or one of the properties failed + // to subscribe. Part of the properties maybe be subscribed successfully if this function + // returns error. Caller is safe to retry since subscribing to an already subscribed property + // is okay. // Returns ok if all the options are parsed correctly and all the properties are subscribed. - android::base::Result<void> subscribe( + VhalResult<void> subscribe( const CallbackType& callback, const std::vector<aidl::android::hardware::automotive::vehicle::SubscribeOptions>& options, bool isContinuousProperty); // Unsubscribes from the properties for the client. - // Returns error if the client was not subscribed before or one of the given property was not - // subscribed. If error is returned, no property would be unsubscribed. + // Returns error if the client was not subscribed before, or one of the given property was not + // subscribed, or one of the property failed to unsubscribe. Caller is safe to retry since + // unsubscribing to an already unsubscribed property is okay (it would be ignored). // Returns ok if all the requested properties for the client are unsubscribed. - android::base::Result<void> unsubscribe(ClientIdType client, - const std::vector<int32_t>& propIds); + VhalResult<void> unsubscribe(ClientIdType client, const std::vector<int32_t>& propIds); // Unsubscribes from all the properties for the client. - // Returns error if the client was not subscribed before. If error is returned, no property - // would be unsubscribed. + // Returns error if the client was not subscribed before or one of the subscribed properties + // for the client failed to unsubscribe. Caller is safe to retry. // Returns ok if all the properties for the client are unsubscribed. - android::base::Result<void> unsubscribe(ClientIdType client); + VhalResult<void> unsubscribe(ClientIdType client); // For a list of updated properties, returns a map that maps clients subscribing to // the updated properties to a list of updated values. This would only return on-change property @@ -83,6 +99,11 @@ class SubscriptionManager final { const std::vector<aidl::android::hardware::automotive::vehicle::VehiclePropValue>& updatedValues); + // Gets the sample rate for the continuous property. Returns {@code std::nullopt} if the + // property has not been subscribed before or is not a continuous property. + std::optional<float> getSampleRate(const ClientIdType& clientId, int32_t propId, + int32_t areaId); + // Checks whether the sample rate is valid. static bool checkSampleRate(float sampleRate); @@ -90,65 +111,28 @@ class SubscriptionManager final { // Friend class for testing. friend class DefaultVehicleHalTest; - struct PropIdAreaId { - int32_t propId; - int32_t areaId; - - bool operator==(const PropIdAreaId& other) const; - }; - - struct PropIdAreaIdHash { - size_t operator()(const PropIdAreaId& propIdAreaId) const; - }; - - // A class to represent a registered subscription. - class Subscription { - public: - Subscription() = default; - - Subscription(const Subscription&) = delete; - - virtual ~Subscription() = default; - - virtual bool isOnChange(); - }; - - // A subscription for OnContinuous property. The registered action would be called recurrently - // until this class is destructed. - class RecurrentSubscription final : public Subscription { - public: - explicit RecurrentSubscription(std::shared_ptr<RecurrentTimer> timer, - std::function<void()>&& action, int64_t interval); - ~RecurrentSubscription(); - - bool isOnChange() override; - - private: - std::shared_ptr<std::function<void()>> mAction; - std::shared_ptr<RecurrentTimer> mTimer; - }; - - // A subscription for OnChange property. - class OnChangeSubscription final : public Subscription { - public: - bool isOnChange() override; - }; + IVehicleHardware* mVehicleHardware; mutable std::mutex mLock; std::unordered_map<PropIdAreaId, std::unordered_map<ClientIdType, CallbackType>, PropIdAreaIdHash> mClientsByPropIdArea GUARDED_BY(mLock); - std::unordered_map<ClientIdType, std::unordered_map<PropIdAreaId, std::unique_ptr<Subscription>, - PropIdAreaIdHash>> - mSubscriptionsByClient GUARDED_BY(mLock); - // RecurrentTimer is thread-safe. - std::shared_ptr<RecurrentTimer> mTimer; - const GetValueFunc mGetValue; + std::unordered_map<ClientIdType, std::unordered_set<PropIdAreaId, PropIdAreaIdHash>> + mSubscribedPropsByClient GUARDED_BY(mLock); + std::unordered_map<PropIdAreaId, ContSubConfigs, PropIdAreaIdHash> mContSubConfigsByPropIdArea + GUARDED_BY(mLock); - static android::base::Result<int64_t> getInterval(float sampleRate); + VhalResult<void> updateSampleRateLocked(const ClientIdType& clientId, + const PropIdAreaId& propIdAreaId, float sampleRate) + REQUIRES(mLock); + VhalResult<void> removeSampleRateLocked(const ClientIdType& clientId, + const PropIdAreaId& propIdAreaId) REQUIRES(mLock); // Checks whether the manager is empty. For testing purpose. bool isEmpty(); + + // Get the interval in nanoseconds accroding to sample rate. + static android::base::Result<int64_t> getInterval(float sampleRate); }; } // namespace vehicle diff --git a/automotive/vehicle/aidl/impl/vhal/src/DefaultVehicleHal.cpp b/automotive/vehicle/aidl/impl/vhal/src/DefaultVehicleHal.cpp index 82f2c1bbcb..b191aef9b3 100644 --- a/automotive/vehicle/aidl/impl/vhal/src/DefaultVehicleHal.cpp +++ b/automotive/vehicle/aidl/impl/vhal/src/DefaultVehicleHal.cpp @@ -144,15 +144,11 @@ DefaultVehicleHal::DefaultVehicleHal(std::unique_ptr<IVehicleHardware> hardware) } mSubscriptionClients = std::make_shared<SubscriptionClients>(mPendingRequestPool); + mSubscriptionClients = std::make_shared<SubscriptionClients>(mPendingRequestPool); auto subscribeIdByClient = std::make_shared<SubscribeIdByClient>(); - // Make a weak copy of IVehicleHardware because subscriptionManager uses IVehicleHardware and - // IVehicleHardware uses subscriptionManager. We want to avoid cyclic reference. - std::weak_ptr<IVehicleHardware> hardwareCopy = mVehicleHardware; - SubscriptionManager::GetValueFunc getValueFunc = std::bind( - &DefaultVehicleHal::getValueFromHardwareCallCallback, hardwareCopy, subscribeIdByClient, - mSubscriptionClients, std::placeholders::_1, std::placeholders::_2); - mSubscriptionManager = std::make_shared<SubscriptionManager>(std::move(getValueFunc)); + IVehicleHardware* hardwarePtr = mVehicleHardware.get(); + mSubscriptionManager = std::make_shared<SubscriptionManager>(hardwarePtr); std::weak_ptr<SubscriptionManager> subscriptionManagerCopy = mSubscriptionManager; mVehicleHardware->registerOnPropertyChangeEvent( @@ -162,11 +158,11 @@ DefaultVehicleHal::DefaultVehicleHal(std::unique_ptr<IVehicleHardware> hardware) })); // Register heartbeat event. - mRecurrentTimer.registerTimerCallback( - HEART_BEAT_INTERVAL_IN_NANO, - std::make_shared<std::function<void()>>([hardwareCopy, subscriptionManagerCopy]() { - checkHealth(hardwareCopy, subscriptionManagerCopy); - })); + mRecurrentAction = + std::make_shared<std::function<void()>>([hardwarePtr, subscriptionManagerCopy]() { + checkHealth(hardwarePtr, subscriptionManagerCopy); + }); + mRecurrentTimer.registerTimerCallback(HEART_BEAT_INTERVAL_IN_NANO, mRecurrentAction); mBinderImpl = std::make_unique<AIBinderImpl>(); mOnBinderDiedUnlinkedHandlerThread = std::thread([this] { onBinderDiedUnlinkedHandler(); }); @@ -183,6 +179,13 @@ DefaultVehicleHal::~DefaultVehicleHal() { if (mOnBinderDiedUnlinkedHandlerThread.joinable()) { mOnBinderDiedUnlinkedHandlerThread.join(); } + // mRecurrentAction uses pointer to mVehicleHardware, so it has to be unregistered before + // mVehicleHardware. + mRecurrentTimer.unregisterTimerCallback(mRecurrentAction); + // mSubscriptionManager uses pointer to mVehicleHardware, so it has to be destroyed before + // mVehicleHardware. + mSubscriptionManager.reset(); + mVehicleHardware.reset(); } void DefaultVehicleHal::onPropertyChangeEvent( @@ -294,43 +297,6 @@ DefaultVehicleHal::getOrCreateClient<SubscriptionClient>( std::unordered_map<const AIBinder*, std::shared_ptr<SubscriptionClient>>* clients, const CallbackType& callback, std::shared_ptr<PendingRequestPool> pendingRequestPool); -void DefaultVehicleHal::getValueFromHardwareCallCallback( - std::weak_ptr<IVehicleHardware> vehicleHardware, - std::shared_ptr<SubscribeIdByClient> subscribeIdByClient, - std::shared_ptr<SubscriptionClients> subscriptionClients, const CallbackType& callback, - const VehiclePropValue& value) { - int64_t subscribeId = subscribeIdByClient->getId(callback); - auto client = subscriptionClients->getClient(callback); - if (client == nullptr) { - ALOGW("subscribe[%" PRId64 "]: the client has died", subscribeId); - return; - } - if (auto addRequestResult = client->addRequests({subscribeId}); !addRequestResult.ok()) { - ALOGE("subscribe[%" PRId64 "]: too many pending requests, ignore the getValue request", - subscribeId); - return; - } - - std::vector<GetValueRequest> hardwareRequests = {{ - .requestId = subscribeId, - .prop = value, - }}; - - std::shared_ptr<IVehicleHardware> hardware = vehicleHardware.lock(); - if (hardware == nullptr) { - ALOGW("the IVehicleHardware is destroyed, DefaultVehicleHal is ending"); - return; - } - if (StatusCode status = hardware->getValues(client->getResultCallback(), hardwareRequests); - status != StatusCode::OK) { - // If the hardware returns error, finish all the pending requests for this request because - // we never expect hardware to call callback for these requests. - client->tryFinishRequests({subscribeId}); - ALOGE("subscribe[%" PRId64 "]: failed to get value from VehicleHardware, code: %d", - subscribeId, toInt(status)); - } -} - void DefaultVehicleHal::setTimeout(int64_t timeoutInNano) { mPendingRequestPool = std::make_unique<PendingRequestPool>(timeoutInNano); } @@ -705,12 +671,13 @@ ScopedAStatus DefaultVehicleHal::subscribe(const CallbackType& callback, // Since we have already check the sample rates, the following functions must succeed. if (!onChangeSubscriptions.empty()) { - mSubscriptionManager->subscribe(callback, onChangeSubscriptions, - /*isContinuousProperty=*/false); + return toScopedAStatus(mSubscriptionManager->subscribe(callback, onChangeSubscriptions, + /*isContinuousProperty=*/false)); } if (!continuousSubscriptions.empty()) { - mSubscriptionManager->subscribe(callback, continuousSubscriptions, - /*isContinuousProperty=*/true); + return toScopedAStatus(mSubscriptionManager->subscribe(callback, + continuousSubscriptions, + /*isContinuousProperty=*/true)); } } return ScopedAStatus::ok(); @@ -718,8 +685,7 @@ ScopedAStatus DefaultVehicleHal::subscribe(const CallbackType& callback, ScopedAStatus DefaultVehicleHal::unsubscribe(const CallbackType& callback, const std::vector<int32_t>& propIds) { - return toScopedAStatus(mSubscriptionManager->unsubscribe(callback->asBinder().get(), propIds), - StatusCode::INVALID_ARG); + return toScopedAStatus(mSubscriptionManager->unsubscribe(callback->asBinder().get(), propIds)); } ScopedAStatus DefaultVehicleHal::returnSharedMemory(const CallbackType&, int64_t) { @@ -763,15 +729,9 @@ VhalResult<void> DefaultVehicleHal::checkReadPermission(const VehiclePropValue& return {}; } -void DefaultVehicleHal::checkHealth(std::weak_ptr<IVehicleHardware> hardware, +void DefaultVehicleHal::checkHealth(IVehicleHardware* hardware, std::weak_ptr<SubscriptionManager> subscriptionManager) { - auto hardwarePtr = hardware.lock(); - if (hardwarePtr == nullptr) { - ALOGW("the VehicleHardware is destroyed, DefaultVehicleHal is ending"); - return; - } - - StatusCode status = hardwarePtr->checkHealth(); + StatusCode status = hardware->checkHealth(); if (status != StatusCode::OK) { ALOGE("VHAL check health returns non-okay status"); return; diff --git a/automotive/vehicle/aidl/impl/vhal/src/SubscriptionManager.cpp b/automotive/vehicle/aidl/impl/vhal/src/SubscriptionManager.cpp index 21bfba6e3a..26944018df 100644 --- a/automotive/vehicle/aidl/impl/vhal/src/SubscriptionManager.cpp +++ b/automotive/vehicle/aidl/impl/vhal/src/SubscriptionManager.cpp @@ -16,8 +16,11 @@ #include "SubscriptionManager.h" -#include <math/HashCombine.h> +#include <android-base/stringprintf.h> #include <utils/Log.h> +#include <utils/SystemClock.h> + +#include <inttypes.h> namespace android { namespace hardware { @@ -31,33 +34,21 @@ constexpr float ONE_SECOND_IN_NANO = 1'000'000'000.; } // namespace using ::aidl::android::hardware::automotive::vehicle::IVehicleCallback; +using ::aidl::android::hardware::automotive::vehicle::StatusCode; using ::aidl::android::hardware::automotive::vehicle::SubscribeOptions; using ::aidl::android::hardware::automotive::vehicle::VehiclePropValue; using ::android::base::Error; using ::android::base::Result; +using ::android::base::StringPrintf; using ::ndk::ScopedAStatus; -bool SubscriptionManager::PropIdAreaId::operator==(const PropIdAreaId& other) const { - return areaId == other.areaId && propId == other.propId; -} - -size_t SubscriptionManager::PropIdAreaIdHash::operator()(PropIdAreaId const& propIdAreaId) const { - size_t res = 0; - hashCombine(res, propIdAreaId.propId); - hashCombine(res, propIdAreaId.areaId); - return res; -} - -SubscriptionManager::SubscriptionManager(GetValueFunc&& action) - : mTimer(std::make_shared<RecurrentTimer>()), mGetValue(std::move(action)) {} +SubscriptionManager::SubscriptionManager(IVehicleHardware* hardware) : mVehicleHardware(hardware) {} SubscriptionManager::~SubscriptionManager() { std::scoped_lock<std::mutex> lockGuard(mLock); mClientsByPropIdArea.clear(); - // RecurrentSubscription has reference to mGetValue, so it must be destroyed before mGetValue is - // destroyed. - mSubscriptionsByClient.clear(); + mSubscribedPropsByClient.clear(); } bool SubscriptionManager::checkSampleRate(float sampleRate) { @@ -76,9 +67,84 @@ Result<int64_t> SubscriptionManager::getInterval(float sampleRate) { return interval; } -Result<void> SubscriptionManager::subscribe(const std::shared_ptr<IVehicleCallback>& callback, - const std::vector<SubscribeOptions>& options, - bool isContinuousProperty) { +void ContSubConfigs::refreshMaxSampleRate() { + float maxSampleRate = 0.; + // This is not called frequently so a brute-focre is okay. More efficient way exists but this + // is simpler. + for (const auto& [_, sampleRate] : mSampleRates) { + if (sampleRate > maxSampleRate) { + maxSampleRate = sampleRate; + } + } + mMaxSampleRate = maxSampleRate; +} + +void ContSubConfigs::addClient(const ClientIdType& clientId, float sampleRate) { + mSampleRates[clientId] = sampleRate; + refreshMaxSampleRate(); +} + +void ContSubConfigs::removeClient(const ClientIdType& clientId) { + mSampleRates.erase(clientId); + refreshMaxSampleRate(); +} + +float ContSubConfigs::getMaxSampleRate() { + return mMaxSampleRate; +} + +VhalResult<void> SubscriptionManager::updateSampleRateLocked(const ClientIdType& clientId, + const PropIdAreaId& propIdAreaId, + float sampleRate) { + // Make a copy so that we don't modify 'mContSubConfigsByPropIdArea' on failure cases. + ContSubConfigs infoCopy = mContSubConfigsByPropIdArea[propIdAreaId]; + infoCopy.addClient(clientId, sampleRate); + if (infoCopy.getMaxSampleRate() == + mContSubConfigsByPropIdArea[propIdAreaId].getMaxSampleRate()) { + mContSubConfigsByPropIdArea[propIdAreaId] = infoCopy; + return {}; + } + float newRate = infoCopy.getMaxSampleRate(); + int32_t propId = propIdAreaId.propId; + int32_t areaId = propIdAreaId.areaId; + if (auto status = mVehicleHardware->updateSampleRate(propId, areaId, newRate); + status != StatusCode::OK) { + return StatusError(status) << StringPrintf("failed to update sample rate for prop: %" PRId32 + ", area" + ": %" PRId32 ", sample rate: %f", + propId, areaId, newRate); + } + mContSubConfigsByPropIdArea[propIdAreaId] = infoCopy; + return {}; +} + +VhalResult<void> SubscriptionManager::removeSampleRateLocked(const ClientIdType& clientId, + const PropIdAreaId& propIdAreaId) { + // Make a copy so that we don't modify 'mContSubConfigsByPropIdArea' on failure cases. + ContSubConfigs infoCopy = mContSubConfigsByPropIdArea[propIdAreaId]; + infoCopy.removeClient(clientId); + if (infoCopy.getMaxSampleRate() == + mContSubConfigsByPropIdArea[propIdAreaId].getMaxSampleRate()) { + mContSubConfigsByPropIdArea[propIdAreaId] = infoCopy; + return {}; + } + float newRate = infoCopy.getMaxSampleRate(); + int32_t propId = propIdAreaId.propId; + int32_t areaId = propIdAreaId.areaId; + if (auto status = mVehicleHardware->updateSampleRate(propId, areaId, newRate); + status != StatusCode::OK) { + return StatusError(status) << StringPrintf("failed to update sample rate for prop: %" PRId32 + ", area" + ": %" PRId32 ", sample rate: %f", + propId, areaId, newRate); + } + mContSubConfigsByPropIdArea[propIdAreaId] = infoCopy; + return {}; +} + +VhalResult<void> SubscriptionManager::subscribe(const std::shared_ptr<IVehicleCallback>& callback, + const std::vector<SubscribeOptions>& options, + bool isContinuousProperty) { std::scoped_lock<std::mutex> lockGuard(mLock); std::vector<int64_t> intervals; @@ -88,109 +154,108 @@ Result<void> SubscriptionManager::subscribe(const std::shared_ptr<IVehicleCallba if (isContinuousProperty) { auto intervalResult = getInterval(sampleRate); if (!intervalResult.ok()) { - return intervalResult.error(); + return StatusError(StatusCode::INVALID_ARG) << intervalResult.error().message(); } - intervals.push_back(intervalResult.value()); } if (option.areaIds.empty()) { ALOGE("area IDs to subscribe must not be empty"); - return Error() << "area IDs to subscribe must not be empty"; + return StatusError(StatusCode::INVALID_ARG) + << "area IDs to subscribe must not be empty"; } } - size_t intervalIndex = 0; ClientIdType clientId = callback->asBinder().get(); + for (const auto& option : options) { int32_t propId = option.propId; const std::vector<int32_t>& areaIds = option.areaIds; - int64_t interval = 0; - if (isContinuousProperty) { - interval = intervals[intervalIndex]; - intervalIndex++; - } for (int32_t areaId : areaIds) { PropIdAreaId propIdAreaId = { .propId = propId, .areaId = areaId, }; if (isContinuousProperty) { - VehiclePropValue propValueRequest{ - .prop = propId, - .areaId = areaId, - }; - mSubscriptionsByClient[clientId][propIdAreaId] = - std::make_unique<RecurrentSubscription>( - mTimer, - [this, callback, propValueRequest] { - mGetValue(callback, propValueRequest); - }, - interval); - } else { - mSubscriptionsByClient[clientId][propIdAreaId] = - std::make_unique<OnChangeSubscription>(); + if (auto result = updateSampleRateLocked(clientId, propIdAreaId, option.sampleRate); + !result.ok()) { + return result; + } } + + mSubscribedPropsByClient[clientId].insert(propIdAreaId); mClientsByPropIdArea[propIdAreaId][clientId] = callback; } } return {}; } -Result<void> SubscriptionManager::unsubscribe(SubscriptionManager::ClientIdType clientId, - const std::vector<int32_t>& propIds) { +VhalResult<void> SubscriptionManager::unsubscribe(SubscriptionManager::ClientIdType clientId, + const std::vector<int32_t>& propIds) { std::scoped_lock<std::mutex> lockGuard(mLock); - if (mSubscriptionsByClient.find(clientId) == mSubscriptionsByClient.end()) { - return Error() << "No property was subscribed for the callback"; + if (mSubscribedPropsByClient.find(clientId) == mSubscribedPropsByClient.end()) { + return StatusError(StatusCode::INVALID_ARG) + << "No property was subscribed for the callback"; } std::unordered_set<int32_t> subscribedPropIds; - for (auto const& [propIdAreaId, _] : mSubscriptionsByClient[clientId]) { + for (auto const& propIdAreaId : mSubscribedPropsByClient[clientId]) { subscribedPropIds.insert(propIdAreaId.propId); } for (int32_t propId : propIds) { if (subscribedPropIds.find(propId) == subscribedPropIds.end()) { - return Error() << "property ID: " << propId << " is not subscribed"; + return StatusError(StatusCode::INVALID_ARG) + << "property ID: " << propId << " is not subscribed"; } } - auto& subscriptions = mSubscriptionsByClient[clientId]; - auto it = subscriptions.begin(); - while (it != subscriptions.end()) { - int32_t propId = it->first.propId; + auto& propIdAreaIds = mSubscribedPropsByClient[clientId]; + auto it = propIdAreaIds.begin(); + while (it != propIdAreaIds.end()) { + int32_t propId = it->propId; if (std::find(propIds.begin(), propIds.end(), propId) != propIds.end()) { - auto& clients = mClientsByPropIdArea[it->first]; + if (auto result = removeSampleRateLocked(clientId, *it); !result.ok()) { + return result; + } + + auto& clients = mClientsByPropIdArea[*it]; clients.erase(clientId); if (clients.empty()) { - mClientsByPropIdArea.erase(it->first); + mClientsByPropIdArea.erase(*it); + mContSubConfigsByPropIdArea.erase(*it); } - it = subscriptions.erase(it); + it = propIdAreaIds.erase(it); } else { it++; } } - if (subscriptions.empty()) { - mSubscriptionsByClient.erase(clientId); + if (propIdAreaIds.empty()) { + mSubscribedPropsByClient.erase(clientId); } return {}; } -Result<void> SubscriptionManager::unsubscribe(SubscriptionManager::ClientIdType clientId) { +VhalResult<void> SubscriptionManager::unsubscribe(SubscriptionManager::ClientIdType clientId) { std::scoped_lock<std::mutex> lockGuard(mLock); - if (mSubscriptionsByClient.find(clientId) == mSubscriptionsByClient.end()) { - return Error() << "No property was subscribed for this client"; + if (mSubscribedPropsByClient.find(clientId) == mSubscribedPropsByClient.end()) { + return StatusError(StatusCode::INVALID_ARG) << "No property was subscribed for this client"; } - auto& subscriptions = mSubscriptionsByClient[clientId]; - for (auto const& [propIdAreaId, _] : subscriptions) { + auto& subscriptions = mSubscribedPropsByClient[clientId]; + for (auto const& propIdAreaId : subscriptions) { + if (auto result = removeSampleRateLocked(clientId, propIdAreaId); !result.ok()) { + return result; + } + auto& clients = mClientsByPropIdArea[propIdAreaId]; clients.erase(clientId); if (clients.empty()) { mClientsByPropIdArea.erase(propIdAreaId); + mContSubConfigsByPropIdArea.erase(propIdAreaId); } } - mSubscriptionsByClient.erase(clientId); + mSubscribedPropsByClient.erase(clientId); return {}; } @@ -208,10 +273,8 @@ SubscriptionManager::getSubscribedClients(const std::vector<VehiclePropValue>& u if (mClientsByPropIdArea.find(propIdAreaId) == mClientsByPropIdArea.end()) { continue; } - for (const auto& [clientId, client] : mClientsByPropIdArea[propIdAreaId]) { - if (!mSubscriptionsByClient[clientId][propIdAreaId]->isOnChange()) { - continue; - } + + for (const auto& [_, client] : mClientsByPropIdArea[propIdAreaId]) { clients[client].push_back(&value); } } @@ -220,25 +283,7 @@ SubscriptionManager::getSubscribedClients(const std::vector<VehiclePropValue>& u bool SubscriptionManager::isEmpty() { std::scoped_lock<std::mutex> lockGuard(mLock); - return mSubscriptionsByClient.empty() && mClientsByPropIdArea.empty(); -} - -SubscriptionManager::RecurrentSubscription::RecurrentSubscription( - std::shared_ptr<RecurrentTimer> timer, std::function<void()>&& action, int64_t interval) - : mAction(std::make_shared<std::function<void()>>(action)), mTimer(timer) { - mTimer->registerTimerCallback(interval, mAction); -} - -SubscriptionManager::RecurrentSubscription::~RecurrentSubscription() { - mTimer->unregisterTimerCallback(mAction); -} - -bool SubscriptionManager::RecurrentSubscription::isOnChange() { - return false; -} - -bool SubscriptionManager::OnChangeSubscription::isOnChange() { - return true; + return mSubscribedPropsByClient.empty() && mClientsByPropIdArea.empty(); } } // namespace vehicle diff --git a/automotive/vehicle/aidl/impl/vhal/test/DefaultVehicleHalTest.cpp b/automotive/vehicle/aidl/impl/vhal/test/DefaultVehicleHalTest.cpp index 49f5b7e435..f48b906503 100644 --- a/automotive/vehicle/aidl/impl/vhal/test/DefaultVehicleHalTest.cpp +++ b/automotive/vehicle/aidl/impl/vhal/test/DefaultVehicleHalTest.cpp @@ -1308,25 +1308,7 @@ TEST_F(DefaultVehicleHalTest, testSubscribeAreaOnChangeAllAreas) { TEST_F(DefaultVehicleHalTest, testSubscribeGlobalContinuous) { VehiclePropValue testValue{ .prop = GLOBAL_CONTINUOUS_PROP, - .value.int32Values = {0}, }; - // Set responses for all the hardware getValues requests. - getHardware()->setGetValueResponder( - [](std::shared_ptr<const IVehicleHardware::GetValuesCallback> callback, - const std::vector<GetValueRequest>& requests) { - std::vector<GetValueResult> results; - for (auto& request : requests) { - VehiclePropValue prop = request.prop; - prop.value.int32Values = {0}; - results.push_back({ - .requestId = request.requestId, - .status = StatusCode::OK, - .prop = prop, - }); - } - (*callback)(results); - return StatusCode::OK; - }); std::vector<SubscribeOptions> options = { { @@ -1353,28 +1335,6 @@ TEST_F(DefaultVehicleHalTest, testSubscribeGlobalContinuous) { } TEST_F(DefaultVehicleHalTest, testSubscribeGlobalContinuousRateOutOfRange) { - VehiclePropValue testValue{ - .prop = GLOBAL_CONTINUOUS_PROP, - .value.int32Values = {0}, - }; - // Set responses for all the hardware getValues requests. - getHardware()->setGetValueResponder( - [](std::shared_ptr<const IVehicleHardware::GetValuesCallback> callback, - const std::vector<GetValueRequest>& requests) { - std::vector<GetValueResult> results; - for (auto& request : requests) { - VehiclePropValue prop = request.prop; - prop.value.int32Values = {0}; - results.push_back({ - .requestId = request.requestId, - .status = StatusCode::OK, - .prop = prop, - }); - } - (*callback)(results); - return StatusCode::OK; - }); - // The maxSampleRate is 100, so the sample rate should be the default max 100. std::vector<SubscribeOptions> options = { { @@ -1398,24 +1358,6 @@ TEST_F(DefaultVehicleHalTest, testSubscribeGlobalContinuousRateOutOfRange) { } TEST_F(DefaultVehicleHalTest, testSubscribeAreaContinuous) { - // Set responses for all the hardware getValues requests. - getHardware()->setGetValueResponder( - [](std::shared_ptr<const IVehicleHardware::GetValuesCallback> callback, - const std::vector<GetValueRequest>& requests) { - std::vector<GetValueResult> results; - for (auto& request : requests) { - VehiclePropValue prop = request.prop; - prop.value.int32Values = {0}; - results.push_back({ - .requestId = request.requestId, - .status = StatusCode::OK, - .prop = prop, - }); - } - (*callback)(results); - return StatusCode::OK; - }); - std::vector<SubscribeOptions> options = { { .propId = AREA_CONTINUOUS_PROP, @@ -1436,6 +1378,8 @@ TEST_F(DefaultVehicleHalTest, testSubscribeAreaContinuous) { // Sleep for 1s, which should generate ~20 events. std::this_thread::sleep_for(std::chrono::seconds(1)); + getClient()->unsubscribe(getCallbackClient(), std::vector<int32_t>({AREA_CONTINUOUS_PROP})); + std::vector<VehiclePropValue> events; while (true) { auto maybeResults = getCallback()->nextOnPropertyEventResults(); @@ -1509,28 +1453,6 @@ TEST_F(DefaultVehicleHalTest, testUnsubscribeOnChange) { } TEST_F(DefaultVehicleHalTest, testUnsubscribeContinuous) { - VehiclePropValue testValue{ - .prop = GLOBAL_CONTINUOUS_PROP, - .value.int32Values = {0}, - }; - // Set responses for all the hardware getValues requests. - getHardware()->setGetValueResponder( - [](std::shared_ptr<const IVehicleHardware::GetValuesCallback> callback, - const std::vector<GetValueRequest>& requests) { - std::vector<GetValueResult> results; - for (auto& request : requests) { - VehiclePropValue prop = request.prop; - prop.value.int32Values = {0}; - results.push_back({ - .requestId = request.requestId, - .status = StatusCode::OK, - .prop = prop, - }); - } - (*callback)(results); - return StatusCode::OK; - }); - std::vector<SubscribeOptions> options = { { .propId = GLOBAL_CONTINUOUS_PROP, @@ -1621,12 +1543,6 @@ TEST_F(DefaultVehicleHalTest, testHeartbeatEvent) { } TEST_F(DefaultVehicleHalTest, testOnBinderDiedUnlinked) { - // First subscribe to a continuous property so that we register a death recipient for our - // client. - VehiclePropValue testValue{ - .prop = GLOBAL_CONTINUOUS_PROP, - .value.int32Values = {0}, - }; // Set responses for all the hardware getValues requests. getHardware()->setGetValueResponder( [](std::shared_ptr<const IVehicleHardware::GetValuesCallback> callback, diff --git a/automotive/vehicle/aidl/impl/vhal/test/MockVehicleHardware.cpp b/automotive/vehicle/aidl/impl/vhal/test/MockVehicleHardware.cpp index 66aef7c2da..4df4e1aea5 100644 --- a/automotive/vehicle/aidl/impl/vhal/test/MockVehicleHardware.cpp +++ b/automotive/vehicle/aidl/impl/vhal/test/MockVehicleHardware.cpp @@ -32,9 +32,14 @@ using ::aidl::android::hardware::automotive::vehicle::StatusCode; using ::aidl::android::hardware::automotive::vehicle::VehiclePropConfig; using ::aidl::android::hardware::automotive::vehicle::VehiclePropValue; +MockVehicleHardware::MockVehicleHardware() { + mRecurrentTimer = std::make_unique<RecurrentTimer>(); +} + MockVehicleHardware::~MockVehicleHardware() { std::unique_lock<std::mutex> lk(mLock); mCv.wait(lk, [this] { return mThreadCount == 0; }); + mRecurrentTimer.reset(); } std::vector<VehiclePropConfig> MockVehicleHardware::getAllPropertyConfigs() const { @@ -83,6 +88,42 @@ StatusCode MockVehicleHardware::checkHealth() { return StatusCode::OK; } +StatusCode MockVehicleHardware::updateSampleRate(int32_t propId, int32_t areaId, float sampleRate) { + std::shared_ptr<std::function<void()>> action; + + { + std::scoped_lock<std::mutex> lockGuard(mLock); + if (mRecurrentActions[propId][areaId] != nullptr) { + // Remove the previous action register for this [propId, areaId]. + mRecurrentTimer->unregisterTimerCallback(mRecurrentActions[propId][areaId]); + } + if (sampleRate == 0) { + return StatusCode::OK; + } + + // We are sure 'propertyChangeCallback' would be alive because we would unregister timer + // before destroying 'this' which owns mPropertyChangeCallback. + const PropertyChangeCallback* propertyChangeCallback = mPropertyChangeCallback.get(); + action = std::make_shared<std::function<void()>>([propertyChangeCallback, propId, areaId] { + std::vector<VehiclePropValue> values = { + { + .prop = propId, + .areaId = areaId, + }, + }; + (*propertyChangeCallback)(values); + }); + // Store the action in a map so that we could remove the action later. + mRecurrentActions[propId][areaId] = action; + } + + // In mock implementation, we generate a new property change event for this property at sample + // rate. + int64_t interval = static_cast<int64_t>(1'000'000'000. / sampleRate); + mRecurrentTimer->registerTimerCallback(interval, action); + return StatusCode::OK; +} + void MockVehicleHardware::registerOnPropertyChangeEvent( std::unique_ptr<const PropertyChangeCallback> callback) { std::scoped_lock<std::mutex> lockGuard(mLock); diff --git a/automotive/vehicle/aidl/impl/vhal/test/MockVehicleHardware.h b/automotive/vehicle/aidl/impl/vhal/test/MockVehicleHardware.h index cb8b6a03f3..743841c216 100644 --- a/automotive/vehicle/aidl/impl/vhal/test/MockVehicleHardware.h +++ b/automotive/vehicle/aidl/impl/vhal/test/MockVehicleHardware.h @@ -18,6 +18,7 @@ #define android_hardware_automotive_vehicle_aidl_impl_vhal_test_MockVehicleHardware_H_ #include <IVehicleHardware.h> +#include <RecurrentTimer.h> #include <VehicleHalTypes.h> #include <android-base/thread_annotations.h> @@ -38,6 +39,8 @@ namespace vehicle { class MockVehicleHardware final : public IVehicleHardware { public: + MockVehicleHardware(); + ~MockVehicleHardware(); std::vector<aidl::android::hardware::automotive::vehicle::VehiclePropConfig> @@ -55,6 +58,8 @@ class MockVehicleHardware final : public IVehicleHardware { void registerOnPropertyChangeEvent( std::unique_ptr<const PropertyChangeCallback> callback) override; void registerOnPropertySetErrorEvent(std::unique_ptr<const PropertySetErrorCallback>) override; + aidl::android::hardware::automotive::vehicle::StatusCode updateSampleRate( + int32_t propId, int32_t areaId, float sampleRate) override; // Test functions. void setPropertyConfigs( @@ -117,6 +122,11 @@ class MockVehicleHardware final : public IVehicleHardware { std::list<std::vector<ResultType>>* storedResponses) const REQUIRES(mLock); DumpResult mDumpResult; + + // RecurrentTimer is thread-safe. + std::shared_ptr<RecurrentTimer> mRecurrentTimer; + std::unordered_map<int32_t, std::unordered_map<int32_t, std::shared_ptr<std::function<void()>>>> + mRecurrentActions GUARDED_BY(mLock); }; } // namespace vehicle diff --git a/automotive/vehicle/aidl/impl/vhal/test/SubscriptionManagerTest.cpp b/automotive/vehicle/aidl/impl/vhal/test/SubscriptionManagerTest.cpp index 2a468f608f..3f593633f9 100644 --- a/automotive/vehicle/aidl/impl/vhal/test/SubscriptionManagerTest.cpp +++ b/automotive/vehicle/aidl/impl/vhal/test/SubscriptionManagerTest.cpp @@ -16,6 +16,7 @@ #include "SubscriptionManager.h" +#include <MockVehicleHardware.h> #include <VehicleHalTypes.h> #include <aidl/android/hardware/automotive/vehicle/BnVehicleCallback.h> @@ -86,19 +87,21 @@ class PropertyCallback final : public BnVehicleCallback { class SubscriptionManagerTest : public testing::Test { public: void SetUp() override { - mManager = std::make_unique<SubscriptionManager>( - [](const std::shared_ptr<IVehicleCallback>& callback, - const VehiclePropValue& value) { - callback->onPropertyEvent( - VehiclePropValues{ - .payloads = {value}, - }, - 0); - }); + mHardware = std::make_shared<MockVehicleHardware>(); + mManager = std::make_unique<SubscriptionManager>(mHardware.get()); mCallback = ndk::SharedRefBase::make<PropertyCallback>(); // Keep the local binder alive. mBinder = mCallback->asBinder(); mCallbackClient = IVehicleCallback::fromBinder(mBinder); + std::shared_ptr<IVehicleCallback> callbackClient = mCallbackClient; + mHardware->registerOnPropertyChangeEvent( + std::make_unique<IVehicleHardware::PropertyChangeCallback>( + [callbackClient](std::vector<VehiclePropValue> updatedValues) { + VehiclePropValues values = { + .payloads = std::move(updatedValues), + }; + callbackClient->onPropertyEvent(values, 0); + })); } SubscriptionManager* getManager() { return mManager.get(); } @@ -115,6 +118,7 @@ class SubscriptionManagerTest : public testing::Test { std::unique_ptr<SubscriptionManager> mManager; std::shared_ptr<PropertyCallback> mCallback; std::shared_ptr<IVehicleCallback> mCallbackClient; + std::shared_ptr<MockVehicleHardware> mHardware; SpAIBinder mBinder; }; diff --git a/automotive/vehicle/vts/src/VtsHalAutomotiveVehicle_TargetTest.cpp b/automotive/vehicle/vts/src/VtsHalAutomotiveVehicle_TargetTest.cpp index c33f3e91d3..c431d85eef 100644 --- a/automotive/vehicle/vts/src/VtsHalAutomotiveVehicle_TargetTest.cpp +++ b/automotive/vehicle/vts/src/VtsHalAutomotiveVehicle_TargetTest.cpp @@ -30,6 +30,7 @@ #include <hidl/ServiceManagement.h> #include <inttypes.h> #include <utils/Log.h> +#include <utils/SystemClock.h> #include <chrono> #include <mutex> @@ -67,6 +68,7 @@ class VtsVehicleCallback final : public ISubscriptionCallback { private: std::mutex mLock; std::unordered_map<int32_t, size_t> mEventsCount GUARDED_BY(mLock); + std::unordered_map<int32_t, std::vector<int64_t>> mEventTimestamps GUARDED_BY(mLock); std::condition_variable mEventCond; public: @@ -74,7 +76,9 @@ class VtsVehicleCallback final : public ISubscriptionCallback { { std::lock_guard<std::mutex> lockGuard(mLock); for (auto& value : values) { - mEventsCount[value->getPropId()] += 1; + int32_t propId = value->getPropId(); + mEventsCount[propId] += 1; + mEventTimestamps[propId].push_back(value->getTimestamp()); } } mEventCond.notify_one(); @@ -94,6 +98,13 @@ class VtsVehicleCallback final : public ISubscriptionCallback { }); } + std::vector<int64_t> getEventTimestamps(int32_t propId) { + { + std::lock_guard<std::mutex> lockGuard(mLock); + return mEventTimestamps[propId]; + } + } + void reset() { std::lock_guard<std::mutex> lockGuard(mLock); mEventsCount.clear(); @@ -285,19 +296,59 @@ TEST_P(VtsHalAutomotiveVehicleTargetTest, subscribeAndUnsubscribe) { int32_t propId = toInt(VehicleProperty::PERF_VEHICLE_SPEED); - std::vector<SubscribeOptions> options = { - SubscribeOptions{.propId = propId, .sampleRate = 10.0}}; + auto propConfigsResult = mVhalClient->getPropConfigs({propId}); + + ASSERT_TRUE(propConfigsResult.ok()) << "Failed to get property config for PERF_VEHICLE_SPEED: " + << "error: " << propConfigsResult.error().message(); + ASSERT_EQ(propConfigsResult.value().size(), 1u) + << "Expect to return 1 config for PERF_VEHICLE_SPEED"; + auto& propConfig = propConfigsResult.value()[0]; + float minSampleRate = propConfig->getMinSampleRate(); + float maxSampleRate = propConfig->getMaxSampleRate(); + + if (minSampleRate < 1) { + GTEST_SKIP() << "Sample rate for vehicle speed < 1 times/sec, skip test since it would " + "take too long"; + } auto client = mVhalClient->getSubscriptionClient(mCallback); ASSERT_NE(client, nullptr) << "Failed to get subscription client"; - auto result = client->subscribe(options); + auto result = client->subscribe({{.propId = propId, .sampleRate = minSampleRate}}); ASSERT_TRUE(result.ok()) << StringPrintf("Failed to subscribe to property: %" PRId32 ", error: %s", propId, result.error().message().c_str()); - ASSERT_TRUE(mCallback->waitForExpectedEvents(propId, 10, std::chrono::seconds(10))) - << "Didn't get enough events for subscription"; + + if (mVhalClient->isAidlVhal()) { + // Skip checking timestamp for HIDL because the behavior for sample rate and timestamp is + // only specified clearly for AIDL. + + // Timeout is 2 seconds, which gives a 1 second buffer. + ASSERT_TRUE(mCallback->waitForExpectedEvents(propId, std::floor(minSampleRate), + std::chrono::seconds(2))) + << "Didn't get enough events for subscribing to minSampleRate"; + } + + result = client->subscribe({{.propId = propId, .sampleRate = maxSampleRate}}); + + ASSERT_TRUE(result.ok()) << StringPrintf("Failed to subscribe to property: %" PRId32 + ", error: %s", + propId, result.error().message().c_str()); + + if (mVhalClient->isAidlVhal()) { + ASSERT_TRUE(mCallback->waitForExpectedEvents(propId, std::floor(maxSampleRate), + std::chrono::seconds(2))) + << "Didn't get enough events for subscribing to maxSampleRate"; + + std::unordered_set<int64_t> timestamps; + // Event event should have a different timestamp. + for (const int64_t& eventTimestamp : mCallback->getEventTimestamps(propId)) { + ASSERT_TRUE(timestamps.find(eventTimestamp) == timestamps.end()) + << "two events for the same property must not have the same timestamp"; + timestamps.insert(eventTimestamp); + } + } result = client->unsubscribe({propId}); ASSERT_TRUE(result.ok()) << StringPrintf("Failed to unsubscribe to property: %" PRId32 @@ -325,6 +376,49 @@ TEST_P(VtsHalAutomotiveVehicleTargetTest, subscribeInvalidProp) { kInvalidProp); } +// Test the timestamp returned in GetValues results is the timestamp when the value is retrieved. +TEST_P(VtsHalAutomotiveVehicleTargetTest, testGetValuesTimestampAIDL) { + if (!mVhalClient->isAidlVhal()) { + GTEST_SKIP() << "Skip checking timestamp for HIDL because the behavior is only specified " + "for AIDL"; + } + + int32_t propId = toInt(VehicleProperty::PARKING_BRAKE_ON); + auto prop = mVhalClient->createHalPropValue(propId); + + auto result = mVhalClient->getValueSync(*prop); + + ASSERT_TRUE(result.ok()) << StringPrintf("Failed to get value for property: %" PRId32 + ", error: %s", + propId, result.error().message().c_str()); + ASSERT_NE(result.value(), nullptr) << "Result value must not be null"; + ASSERT_EQ(result.value()->getInt32Values().size(), 1u) << "Result must contain 1 int value"; + + bool parkBrakeOnValue1 = (result.value()->getInt32Values()[0] == 1); + int64_t timestampValue1 = result.value()->getTimestamp(); + + result = mVhalClient->getValueSync(*prop); + + ASSERT_TRUE(result.ok()) << StringPrintf("Failed to get value for property: %" PRId32 + ", error: %s", + propId, result.error().message().c_str()); + ASSERT_NE(result.value(), nullptr) << "Result value must not be null"; + ASSERT_EQ(result.value()->getInt32Values().size(), 1u) << "Result must contain 1 int value"; + + bool parkBarkeOnValue2 = (result.value()->getInt32Values()[0] == 1); + int64_t timestampValue2 = result.value()->getTimestamp(); + + if (parkBarkeOnValue2 == parkBrakeOnValue1) { + ASSERT_EQ(timestampValue2, timestampValue1) + << "getValue result must contain a timestamp updated when the value was updated, if" + "the value does not change, expect the same timestamp"; + } else { + ASSERT_GT(timestampValue2, timestampValue1) + << "getValue result must contain a timestamp updated when the value was updated, if" + "the value changes, expect the newer value has a larger timestamp"; + } +} + std::vector<ServiceDescriptor> getDescriptors() { std::vector<ServiceDescriptor> descriptors; for (std::string name : getAidlHalInstanceNames(IVehicle::descriptor)) { diff --git a/bluetooth/1.0/default/test/fuzzer/Android.bp b/bluetooth/1.0/default/test/fuzzer/Android.bp index 81f328e4de..691136fe55 100644 --- a/bluetooth/1.0/default/test/fuzzer/Android.bp +++ b/bluetooth/1.0/default/test/fuzzer/Android.bp @@ -46,7 +46,6 @@ cc_fuzz { "android.hardware.bluetooth-async", "android.hardware.bluetooth-hci", "libcutils", - "libutils", ], shared_libs: [ "android.hardware.bluetooth@1.0", @@ -54,6 +53,7 @@ cc_fuzz { "libhidlbase", "libbt-vendor-fuzz", "liblog", + "libutils", ], fuzz_config: { cc: [ diff --git a/bluetooth/audio/aidl/default/BluetoothAudioProvider.cpp b/bluetooth/audio/aidl/default/BluetoothAudioProvider.cpp index 0dd814828c..2a88959af5 100644 --- a/bluetooth/audio/aidl/default/BluetoothAudioProvider.cpp +++ b/bluetooth/audio/aidl/default/BluetoothAudioProvider.cpp @@ -45,6 +45,7 @@ ndk::ScopedAStatus BluetoothAudioProvider::startSession( latency_modes_ = latencyModes; audio_config_ = std::make_unique<AudioConfiguration>(audio_config); stack_iface_ = host_if; + is_binder_died = false; AIBinder_linkToDeath(stack_iface_->asBinder().get(), death_recipient_.get(), this); @@ -59,8 +60,10 @@ ndk::ScopedAStatus BluetoothAudioProvider::endSession() { if (stack_iface_ != nullptr) { BluetoothAudioSessionReport::OnSessionEnded(session_type_); - AIBinder_unlinkToDeath(stack_iface_->asBinder().get(), - death_recipient_.get(), this); + if (!is_binder_died) { + AIBinder_unlinkToDeath(stack_iface_->asBinder().get(), + death_recipient_.get(), this); + } } else { LOG(INFO) << __func__ << " - SessionType=" << toString(session_type_) << " has NO session"; @@ -147,6 +150,7 @@ void BluetoothAudioProvider::binderDiedCallbackAidl(void* ptr) { LOG(ERROR) << __func__ << ": Null AudioProvider HAL died"; return; } + provider->is_binder_died = true; provider->endSession(); } diff --git a/bluetooth/audio/aidl/default/BluetoothAudioProvider.h b/bluetooth/audio/aidl/default/BluetoothAudioProvider.h index a9f830af78..dbfff7d26c 100644 --- a/bluetooth/audio/aidl/default/BluetoothAudioProvider.h +++ b/bluetooth/audio/aidl/default/BluetoothAudioProvider.h @@ -62,6 +62,7 @@ class BluetoothAudioProvider : public BnBluetoothAudioProvider { std::unique_ptr<AudioConfiguration> audio_config_ = nullptr; SessionType session_type_; std::vector<LatencyMode> latency_modes_; + bool is_binder_died = false; }; } // namespace audio diff --git a/graphics/composer/aidl/include/android/hardware/graphics/composer3/ComposerClientWriter.h b/graphics/composer/aidl/include/android/hardware/graphics/composer3/ComposerClientWriter.h index ae17c51ea2..1d81f7b602 100644 --- a/graphics/composer/aidl/include/android/hardware/graphics/composer3/ComposerClientWriter.h +++ b/graphics/composer/aidl/include/android/hardware/graphics/composer3/ComposerClientWriter.h @@ -80,8 +80,9 @@ class ComposerClientWriter { getDisplayCommand(display).colorTransformMatrix.emplace(std::move(matVec)); } - void setDisplayBrightness(int64_t display, float brightness) { - getDisplayCommand(display).brightness.emplace(DisplayBrightness{.brightness = brightness}); + void setDisplayBrightness(int64_t display, float brightness, float brightnessNits) { + getDisplayCommand(display).brightness.emplace( + DisplayBrightness{.brightness = brightness, .brightnessNits = brightnessNits}); } void setClientTarget(int64_t display, uint32_t slot, const native_handle_t* target, diff --git a/graphics/composer/aidl/vts/VtsHalGraphicsComposer3_ReadbackTest.cpp b/graphics/composer/aidl/vts/VtsHalGraphicsComposer3_ReadbackTest.cpp index 147a9eec82..c081199ba6 100644 --- a/graphics/composer/aidl/vts/VtsHalGraphicsComposer3_ReadbackTest.cpp +++ b/graphics/composer/aidl/vts/VtsHalGraphicsComposer3_ReadbackTest.cpp @@ -974,7 +974,7 @@ TEST_P(GraphicsCompositionTest, SetLayerBrightnessDims) { // Preconditions to successfully run are knowing the max brightness and successfully applying // the max brightness ASSERT_GT(maxBrightnessNits, 0.f); - mWriter.setDisplayBrightness(getPrimaryDisplayId(), /*brightness*/ 1.f); + mWriter.setDisplayBrightness(getPrimaryDisplayId(), /*brightness*/ 1.f, maxBrightnessNits); execute(); ASSERT_TRUE(mReader.takeErrors().empty()); diff --git a/graphics/composer/aidl/vts/VtsHalGraphicsComposer3_TargetTest.cpp b/graphics/composer/aidl/vts/VtsHalGraphicsComposer3_TargetTest.cpp index 2d08ac611b..2cae5a2c93 100644 --- a/graphics/composer/aidl/vts/VtsHalGraphicsComposer3_TargetTest.cpp +++ b/graphics/composer/aidl/vts/VtsHalGraphicsComposer3_TargetTest.cpp @@ -1374,7 +1374,7 @@ TEST_P(GraphicsComposerAidlCommandTest, SetDisplayBrightness) { bool brightnessSupport = std::find(capabilities.begin(), capabilities.end(), DisplayCapability::BRIGHTNESS) != capabilities.end(); if (!brightnessSupport) { - mWriter.setDisplayBrightness(getPrimaryDisplayId(), /*brightness*/ 0.5f); + mWriter.setDisplayBrightness(getPrimaryDisplayId(), /*brightness*/ 0.5f, -1.f); execute(); const auto errors = mReader.takeErrors(); EXPECT_EQ(1, errors.size()); @@ -1383,23 +1383,23 @@ TEST_P(GraphicsComposerAidlCommandTest, SetDisplayBrightness) { return; } - mWriter.setDisplayBrightness(getPrimaryDisplayId(), /*brightness*/ 0.0f); + mWriter.setDisplayBrightness(getPrimaryDisplayId(), /*brightness*/ 0.0f, -1.f); execute(); EXPECT_TRUE(mReader.takeErrors().empty()); - mWriter.setDisplayBrightness(getPrimaryDisplayId(), /*brightness*/ 0.5f); + mWriter.setDisplayBrightness(getPrimaryDisplayId(), /*brightness*/ 0.5f, -1.f); execute(); EXPECT_TRUE(mReader.takeErrors().empty()); - mWriter.setDisplayBrightness(getPrimaryDisplayId(), /*brightness*/ 1.0f); + mWriter.setDisplayBrightness(getPrimaryDisplayId(), /*brightness*/ 1.0f, -1.f); execute(); EXPECT_TRUE(mReader.takeErrors().empty()); - mWriter.setDisplayBrightness(getPrimaryDisplayId(), /*brightness*/ -1.0f); + mWriter.setDisplayBrightness(getPrimaryDisplayId(), /*brightness*/ -1.0f, -1.f); execute(); EXPECT_TRUE(mReader.takeErrors().empty()); - mWriter.setDisplayBrightness(getPrimaryDisplayId(), /*brightness*/ 2.0f); + mWriter.setDisplayBrightness(getPrimaryDisplayId(), /*brightness*/ 2.0f, -1.f); execute(); { const auto errors = mReader.takeErrors(); @@ -1407,7 +1407,7 @@ TEST_P(GraphicsComposerAidlCommandTest, SetDisplayBrightness) { EXPECT_EQ(IComposerClient::EX_BAD_PARAMETER, errors[0].errorCode); } - mWriter.setDisplayBrightness(getPrimaryDisplayId(), /*brightness*/ -2.0f); + mWriter.setDisplayBrightness(getPrimaryDisplayId(), /*brightness*/ -2.0f, -1.f); execute(); { const auto errors = mReader.takeErrors(); diff --git a/graphics/mapper/3.0/utils/vts/MapperVts.cpp b/graphics/mapper/3.0/utils/vts/MapperVts.cpp index de886a9c70..c470a4aca0 100644 --- a/graphics/mapper/3.0/utils/vts/MapperVts.cpp +++ b/graphics/mapper/3.0/utils/vts/MapperVts.cpp @@ -14,7 +14,9 @@ * limitations under the License. */ +#include <android-base/properties.h> #include <mapper-vts/3.0/MapperVts.h> +#include "gtest/gtest.h" namespace android { namespace hardware { @@ -94,23 +96,31 @@ std::vector<const native_handle_t*> Gralloc::allocate(const BufferDescriptor& de std::vector<const native_handle_t*> bufferHandles; bufferHandles.reserve(count); mAllocator->allocate( - descriptor, count, - [&](const auto& tmpError, const auto& tmpStride, const auto& tmpBuffers) { - ASSERT_EQ(Error::NONE, tmpError) << "failed to allocate buffers"; - ASSERT_EQ(count, tmpBuffers.size()) << "invalid buffer array"; - - for (uint32_t i = 0; i < count; i++) { - if (import) { - ASSERT_NO_FATAL_FAILURE(bufferHandles.push_back(importBuffer(tmpBuffers[i]))); - } else { - ASSERT_NO_FATAL_FAILURE(bufferHandles.push_back(cloneBuffer(tmpBuffers[i]))); + descriptor, count, + [&](const auto& tmpError, const auto& tmpStride, const auto& tmpBuffers) { + if (tmpError != Error::NONE) { + if (base::GetIntProperty("ro.vendor.build.version.sdk", 0, 0, INT_MAX) < 33) { + GTEST_SKIP() << "Old vendor grallocs may not support P010"; + } else { + GTEST_FAIL() << "failed to allocate buffers"; + } + } + ASSERT_EQ(count, tmpBuffers.size()) << "invalid buffer array"; + + for (uint32_t i = 0; i < count; i++) { + if (import) { + ASSERT_NO_FATAL_FAILURE( + bufferHandles.push_back(importBuffer(tmpBuffers[i]))); + } else { + ASSERT_NO_FATAL_FAILURE( + bufferHandles.push_back(cloneBuffer(tmpBuffers[i]))); + } } - } - if (outStride) { - *outStride = tmpStride; - } - }); + if (outStride) { + *outStride = tmpStride; + } + }); if (::testing::Test::HasFatalFailure()) { bufferHandles.clear(); @@ -127,7 +137,7 @@ const native_handle_t* Gralloc::allocate(const IMapper::BufferDescriptorInfo& de } auto buffers = allocate(descriptor, 1, import, outStride); - if (::testing::Test::HasFatalFailure()) { + if (::testing::Test::HasFatalFailure() || ::testing::Test::IsSkipped()) { return nullptr; } diff --git a/graphics/mapper/3.0/vts/functional/VtsHalGraphicsMapperV3_0TargetTest.cpp b/graphics/mapper/3.0/vts/functional/VtsHalGraphicsMapperV3_0TargetTest.cpp index 6c90af407a..3b1bfab867 100644 --- a/graphics/mapper/3.0/vts/functional/VtsHalGraphicsMapperV3_0TargetTest.cpp +++ b/graphics/mapper/3.0/vts/functional/VtsHalGraphicsMapperV3_0TargetTest.cpp @@ -337,6 +337,10 @@ TEST_P(GraphicsMapperHidlTest, LockYCbCrP010) { uint32_t stride; ASSERT_NO_FATAL_FAILURE(bufferHandle = mGralloc->allocate(info, true, &stride)); + if (::testing::Test::IsSkipped()) { + GTEST_SKIP(); + } + ASSERT_NE(nullptr, bufferHandle); const IMapper::Rect region{0, 0, static_cast<int32_t>(info.width), diff --git a/graphics/mapper/4.0/utils/vts/MapperVts.cpp b/graphics/mapper/4.0/utils/vts/MapperVts.cpp index 901f0e3ae7..4a6f68da26 100644 --- a/graphics/mapper/4.0/utils/vts/MapperVts.cpp +++ b/graphics/mapper/4.0/utils/vts/MapperVts.cpp @@ -14,6 +14,7 @@ * limitations under the License. */ +#include <android-base/properties.h> #include <gralloctypes/Gralloc4.h> #include <mapper-vts/4.0/MapperVts.h> @@ -95,7 +96,14 @@ std::vector<const native_handle_t*> Gralloc::allocate(const BufferDescriptor& de return; } - ASSERT_EQ(Error::NONE, tmpError) << "failed to allocate buffers"; + if (tmpError != Error::NONE) { + if (base::GetIntProperty("ro.vendor.build.version.sdk", 0, 0, + INT_MAX) < 33) { + GTEST_SKIP() << "Old vendor grallocs may not support P010"; + } else { + GTEST_FAIL() << "failed to allocate buffers"; + } + } ASSERT_EQ(count, tmpBuffers.size()) << "invalid buffer array"; for (uint32_t i = 0; i < count; i++) { @@ -133,11 +141,7 @@ const native_handle_t* Gralloc::allocate(const IMapper::BufferDescriptorInfo& de } auto buffers = allocate(descriptor, 1, import, tolerance, outStride); - if (::testing::Test::HasFatalFailure()) { - return nullptr; - } - - if (buffers.size() != 1) { + if (::testing::Test::HasFatalFailure() || ::testing::Test::IsSkipped() || buffers.size() != 1) { return nullptr; } return buffers[0]; diff --git a/graphics/mapper/4.0/vts/functional/VtsHalGraphicsMapperV4_0TargetTest.cpp b/graphics/mapper/4.0/vts/functional/VtsHalGraphicsMapperV4_0TargetTest.cpp index 463b565c2c..8f440e4d1e 100644 --- a/graphics/mapper/4.0/vts/functional/VtsHalGraphicsMapperV4_0TargetTest.cpp +++ b/graphics/mapper/4.0/vts/functional/VtsHalGraphicsMapperV4_0TargetTest.cpp @@ -999,10 +999,13 @@ TEST_P(GraphicsMapperHidlTest, Lock_YCBCR_P010) { auto info = mDummyDescriptorInfo; info.format = PixelFormat::YCBCR_P010; - const native_handle_t* bufferHandle; uint32_t stride; - ASSERT_NO_FATAL_FAILURE( - bufferHandle = mGralloc->allocate(info, true, Tolerance::kToleranceStrict, &stride)); + const native_handle_t* bufferHandle = + mGralloc->allocate(info, true, Tolerance::kToleranceStrict, &stride); + + if (::testing::Test::IsSkipped()) { + GTEST_SKIP(); + } const IMapper::Rect region{0, 0, static_cast<int32_t>(info.width), static_cast<int32_t>(info.height)}; diff --git a/keymaster/4.0/support/fuzzer/Android.bp b/keymaster/4.0/support/fuzzer/Android.bp index 3a3f4d5589..8bc681a46b 100644 --- a/keymaster/4.0/support/fuzzer/Android.bp +++ b/keymaster/4.0/support/fuzzer/Android.bp @@ -30,12 +30,12 @@ cc_defaults { "libbase", "liblog", "libkeymaster4support", - "libutils", ], shared_libs: [ "android.hardware.keymaster@4.0", "libcrypto", "libhidlbase", + "libutils", ], fuzz_config: { cc: [ diff --git a/keymaster/4.0/vts/functional/keymaster_hidl_hal_test.cpp b/keymaster/4.0/vts/functional/keymaster_hidl_hal_test.cpp index 22aa0f9b3d..bf568600d6 100644 --- a/keymaster/4.0/vts/functional/keymaster_hidl_hal_test.cpp +++ b/keymaster/4.0/vts/functional/keymaster_hidl_hal_test.cpp @@ -2866,8 +2866,8 @@ TEST_P(EncryptionOperationsTest, AesEcbPkcs7PaddingCorrupted) { EXPECT_EQ(ErrorCode::OK, Begin(KeyPurpose::DECRYPT, params)); string plaintext; - ErrorCode error = Finish(message, &plaintext); - if (error == ErrorCode::INVALID_INPUT_LENGTH) { + ErrorCode error = Finish(ciphertext, &plaintext); + if (error == ErrorCode::INVALID_ARGUMENT) { // This is the expected error, we can exit the test now. return; } else { diff --git a/media/omx/1.0/vts/functional/store/VtsHalMediaOmxV1_0TargetStoreTest.cpp b/media/omx/1.0/vts/functional/store/VtsHalMediaOmxV1_0TargetStoreTest.cpp index d9a6363ec9..5fa13e7c5a 100644 --- a/media/omx/1.0/vts/functional/store/VtsHalMediaOmxV1_0TargetStoreTest.cpp +++ b/media/omx/1.0/vts/functional/store/VtsHalMediaOmxV1_0TargetStoreTest.cpp @@ -24,6 +24,7 @@ #include <android-base/strings.h> #include <android/api-level.h> +#include <VtsCoreUtil.h> #include <android/hardware/media/omx/1.0/IOmx.h> #include <android/hardware/media/omx/1.0/IOmxNode.h> #include <android/hardware/media/omx/1.0/IOmxObserver.h> @@ -377,6 +378,10 @@ static int getFirstApiLevel() { return android::base::GetIntProperty("ro.product.first_api_level", __ANDROID_API_T__); } +static bool isTV() { + return testing::deviceSupportsFeature("android.software.leanback"); +} + // list components and roles. TEST_P(StoreHidlTest, OmxCodecAllowedTest) { hidl_vec<IOmx::ComponentInfo> componentInfos = getComponentInfoList(omx); @@ -384,9 +389,16 @@ TEST_P(StoreHidlTest, OmxCodecAllowedTest) { for (std::string role : info.mRoles) { if (role.find("video_decoder") != std::string::npos || role.find("video_encoder") != std::string::npos) { - ASSERT_LT(getFirstApiLevel(), __ANDROID_API_S__) - << " Component: " << info.mName.c_str() << " Role: " << role.c_str() - << " not allowed for devices launching with Android S and above"; + // Codec2 is not mandatory on Android TV devices that launched with Android S + if (isTV()) { + ASSERT_LT(getFirstApiLevel(), __ANDROID_API_T__) + << " Component: " << info.mName.c_str() << " Role: " << role.c_str() + << " not allowed for devices launching with Android T and above"; + } else { + ASSERT_LT(getFirstApiLevel(), __ANDROID_API_S__) + << " Component: " << info.mName.c_str() << " Role: " << role.c_str() + << " not allowed for devices launching with Android S and above"; + } } if (role.find("audio_decoder") != std::string::npos || role.find("audio_encoder") != std::string::npos) { diff --git a/radio/aidl/Android.bp b/radio/aidl/Android.bp index 24764307f1..c3288799a0 100644 --- a/radio/aidl/Android.bp +++ b/radio/aidl/Android.bp @@ -10,6 +10,7 @@ package { aidl_interface { name: "android.hardware.radio", vendor_available: true, + host_supported: true, srcs: ["android/hardware/radio/*.aidl"], stability: "vintf", backend: { @@ -30,6 +31,7 @@ aidl_interface { aidl_interface { name: "android.hardware.radio.config", vendor_available: true, + host_supported: true, srcs: ["android/hardware/radio/config/*.aidl"], stability: "vintf", imports: ["android.hardware.radio"], @@ -58,6 +60,7 @@ aidl_interface { aidl_interface { name: "android.hardware.radio.data", vendor_available: true, + host_supported: true, srcs: ["android/hardware/radio/data/*.aidl"], stability: "vintf", imports: ["android.hardware.radio"], @@ -86,6 +89,7 @@ aidl_interface { aidl_interface { name: "android.hardware.radio.messaging", vendor_available: true, + host_supported: true, srcs: ["android/hardware/radio/messaging/*.aidl"], stability: "vintf", imports: ["android.hardware.radio"], @@ -107,6 +111,7 @@ aidl_interface { aidl_interface { name: "android.hardware.radio.modem", vendor_available: true, + host_supported: true, srcs: ["android/hardware/radio/modem/*.aidl"], stability: "vintf", imports: ["android.hardware.radio"], @@ -135,6 +140,7 @@ aidl_interface { aidl_interface { name: "android.hardware.radio.network", vendor_available: true, + host_supported: true, srcs: ["android/hardware/radio/network/*.aidl"], stability: "vintf", imports: ["android.hardware.radio"], @@ -163,6 +169,7 @@ aidl_interface { aidl_interface { name: "android.hardware.radio.sim", vendor_available: true, + host_supported: true, srcs: ["android/hardware/radio/sim/*.aidl"], stability: "vintf", imports: [ @@ -197,6 +204,7 @@ aidl_interface { aidl_interface { name: "android.hardware.radio.voice", vendor_available: true, + host_supported: true, srcs: ["android/hardware/radio/voice/*.aidl"], stability: "vintf", imports: ["android.hardware.radio"], diff --git a/radio/aidl/vts/radio_config_test.cpp b/radio/aidl/vts/radio_config_test.cpp index 5e1c811d91..258b172918 100644 --- a/radio/aidl/vts/radio_config_test.cpp +++ b/radio/aidl/vts/radio_config_test.cpp @@ -59,6 +59,7 @@ TEST_P(RadioConfigTest, getHalDeviceCapabilities) { serial = GetRandomSerialNumber(); ndk::ScopedAStatus res = radio_config->getHalDeviceCapabilities(serial); ASSERT_OK(res); + EXPECT_EQ(std::cv_status::no_timeout, wait()); ALOGI("getHalDeviceCapabilities, rspInfo.error = %s\n", toString(radioRsp_config->rspInfo.error).c_str()); } @@ -70,6 +71,7 @@ TEST_P(RadioConfigTest, getSimSlotsStatus) { serial = GetRandomSerialNumber(); ndk::ScopedAStatus res = radio_config->getSimSlotsStatus(serial); ASSERT_OK(res); + EXPECT_EQ(std::cv_status::no_timeout, wait()); ALOGI("getSimSlotsStatus, rspInfo.error = %s\n", toString(radioRsp_config->rspInfo.error).c_str()); } @@ -166,31 +168,60 @@ TEST_P(RadioConfigTest, setPreferredDataModem_invalidArgument) { * Test IRadioConfig.setSimSlotsMapping() for the response returned. */ TEST_P(RadioConfigTest, setSimSlotsMapping) { - serial = GetRandomSerialNumber(); - SlotPortMapping slotPortMapping; - slotPortMapping.physicalSlotId = 0; - slotPortMapping.portId = 0; - std::vector<SlotPortMapping> slotPortMappingList = {slotPortMapping}; - if (isDsDsEnabled()) { - slotPortMapping.physicalSlotId = 1; - slotPortMappingList.push_back(slotPortMapping); - } else if (isTsTsEnabled()) { - slotPortMapping.physicalSlotId = 1; - slotPortMappingList.push_back(slotPortMapping); - slotPortMapping.physicalSlotId = 2; - slotPortMappingList.push_back(slotPortMapping); - } - ndk::ScopedAStatus res = radio_config->setSimSlotsMapping(serial, slotPortMappingList); - ASSERT_OK(res); - EXPECT_EQ(std::cv_status::no_timeout, wait()); - EXPECT_EQ(RadioResponseType::SOLICITED, radioRsp_config->rspInfo.type); - EXPECT_EQ(serial, radioRsp_config->rspInfo.serial); - ALOGI("setSimSlotsMapping, rspInfo.error = %s\n", - toString(radioRsp_config->rspInfo.error).c_str()); - ASSERT_TRUE(CheckAnyOfErrors(radioRsp_config->rspInfo.error, {RadioError::NONE})); + // get slot status and set SIM slots mapping based on the result. + updateSimSlotStatus(); + if (radioRsp_config->rspInfo.error == RadioError::NONE) { + SlotPortMapping slotPortMapping; + // put invalid value at first and adjust by slotStatusResponse. + slotPortMapping.physicalSlotId = -1; + slotPortMapping.portId = -1; + std::vector<SlotPortMapping> slotPortMappingList = {slotPortMapping}; + if (isDsDsEnabled()) { + slotPortMappingList.push_back(slotPortMapping); + } else if (isTsTsEnabled()) { + slotPortMappingList.push_back(slotPortMapping); + slotPortMappingList.push_back(slotPortMapping); + } + for (size_t i = 0; i < radioRsp_config->simSlotStatus.size(); i++) { + ASSERT_TRUE(radioRsp_config->simSlotStatus[i].portInfo.size() > 0); + for (size_t j = 0; j < radioRsp_config->simSlotStatus[i].portInfo.size(); j++) { + if (radioRsp_config->simSlotStatus[i].portInfo[j].portActive) { + int32_t logicalSlotId = + radioRsp_config->simSlotStatus[i].portInfo[j].logicalSlotId; + // logicalSlotId should be 0 or positive numbers if the port + // is active. + EXPECT_GE(logicalSlotId, 0); + // logicalSlotId should be less than the maximum number of + // supported SIM slots. + EXPECT_LT(logicalSlotId, slotPortMappingList.size()); + if (logicalSlotId >= 0 && logicalSlotId < slotPortMappingList.size()) { + slotPortMappingList[logicalSlotId].physicalSlotId = i; + slotPortMappingList[logicalSlotId].portId = j; + } + } + } + } - // Give some time for modem to fully switch SIM configuration - sleep(MODEM_SET_SIM_SLOT_MAPPING_DELAY_IN_SECONDS); + // set SIM slots mapping + for (size_t i = 0; i < slotPortMappingList.size(); i++) { + // physicalSlotId and portId should be 0 or positive numbers for the + // input of setSimSlotsMapping. + EXPECT_GE(slotPortMappingList[i].physicalSlotId, 0); + EXPECT_GE(slotPortMappingList[i].portId, 0); + } + serial = GetRandomSerialNumber(); + ndk::ScopedAStatus res = radio_config->setSimSlotsMapping(serial, slotPortMappingList); + ASSERT_OK(res); + EXPECT_EQ(std::cv_status::no_timeout, wait()); + EXPECT_EQ(RadioResponseType::SOLICITED, radioRsp_config->rspInfo.type); + EXPECT_EQ(serial, radioRsp_config->rspInfo.serial); + ALOGI("setSimSlotsMapping, rspInfo.error = %s\n", + toString(radioRsp_config->rspInfo.error).c_str()); + ASSERT_TRUE(CheckAnyOfErrors(radioRsp_config->rspInfo.error, {RadioError::NONE})); + + // Give some time for modem to fully switch SIM configuration + sleep(MODEM_SET_SIM_SLOT_MAPPING_DELAY_IN_SECONDS); + } } /* @@ -207,12 +238,24 @@ TEST_P(RadioConfigTest, checkPortInfoExistsAndPortActive) { EXPECT_EQ(RadioResponseType::SOLICITED, radioRsp_config->rspInfo.type); EXPECT_EQ(serial, radioRsp_config->rspInfo.serial); if (radioRsp_config->rspInfo.error == RadioError::NONE) { + uint8_t simCount = 0; // check if cardState is present, portInfo size should be more than 0 for (const SimSlotStatus& slotStatusResponse : radioRsp_config->simSlotStatus) { if (slotStatusResponse.cardState == CardStatus::STATE_PRESENT) { ASSERT_TRUE(slotStatusResponse.portInfo.size() > 0); - ASSERT_TRUE(slotStatusResponse.portInfo[0].portActive); + for (const SimPortInfo& simPortInfo : slotStatusResponse.portInfo) { + if (simPortInfo.portActive) { + simCount++; + } + } } } + if (isSsSsEnabled()) { + EXPECT_EQ(1, simCount); + } else if (isDsDsEnabled()) { + EXPECT_EQ(2, simCount); + } else if (isTsTsEnabled()) { + EXPECT_EQ(3, simCount); + } } } diff --git a/radio/aidl/vts/radio_modem_response.cpp b/radio/aidl/vts/radio_modem_response.cpp index d2715a8198..20b44c5831 100644 --- a/radio/aidl/vts/radio_modem_response.cpp +++ b/radio/aidl/vts/radio_modem_response.cpp @@ -24,6 +24,7 @@ ndk::ScopedAStatus RadioModemResponse::acknowledgeRequest(int32_t /*serial*/) { ndk::ScopedAStatus RadioModemResponse::enableModemResponse(const RadioResponseInfo& info) { rspInfo = info; + enableModemResponseToggle = !enableModemResponseToggle; parent_modem.notify(info.serial); return ndk::ScopedAStatus::ok(); } diff --git a/radio/aidl/vts/radio_modem_utils.h b/radio/aidl/vts/radio_modem_utils.h index 8779e0cbd2..49e1891212 100644 --- a/radio/aidl/vts/radio_modem_utils.h +++ b/radio/aidl/vts/radio_modem_utils.h @@ -37,7 +37,7 @@ class RadioModemResponse : public BnRadioModemResponse { RadioResponseInfo rspInfo; bool isModemEnabled; - bool enableModemResponseToggle; + bool enableModemResponseToggle = false; virtual ndk::ScopedAStatus acknowledgeRequest(int32_t serial) override; diff --git a/radio/aidl/vts/radio_network_test.cpp b/radio/aidl/vts/radio_network_test.cpp index e1d508d1d3..0bae3748da 100644 --- a/radio/aidl/vts/radio_network_test.cpp +++ b/radio/aidl/vts/radio_network_test.cpp @@ -592,6 +592,89 @@ TEST_P(RadioNetworkTest, setSignalStrengthReportingCriteria_NGRAN_SSSINR) { } /* + * Test IRadioNetwork.setSignalStrengthReportingCriteria() for multi-RANs per request + */ +TEST_P(RadioNetworkTest, setSignalStrengthReportingCriteria_multiRansPerRequest) { + SignalThresholdInfo signalThresholdInfoGeran; + signalThresholdInfoGeran.signalMeasurement = SignalThresholdInfo::SIGNAL_MEASUREMENT_TYPE_RSSI; + signalThresholdInfoGeran.hysteresisMs = 5000; + signalThresholdInfoGeran.hysteresisDb = 2; + signalThresholdInfoGeran.thresholds = {-109, -103, -97, -89}; + signalThresholdInfoGeran.isEnabled = true; + signalThresholdInfoGeran.ran = AccessNetwork::GERAN; + + SignalThresholdInfo signalThresholdInfoUtran; + signalThresholdInfoUtran.signalMeasurement = SignalThresholdInfo::SIGNAL_MEASUREMENT_TYPE_RSCP; + signalThresholdInfoUtran.hysteresisMs = 5000; + signalThresholdInfoUtran.hysteresisDb = 2; + signalThresholdInfoUtran.thresholds = {-110, -97, -73, -49, -25}; + signalThresholdInfoUtran.isEnabled = true; + signalThresholdInfoUtran.ran = AccessNetwork::UTRAN; + + SignalThresholdInfo signalThresholdInfoEutran; + signalThresholdInfoEutran.signalMeasurement = SignalThresholdInfo::SIGNAL_MEASUREMENT_TYPE_RSRP; + signalThresholdInfoEutran.hysteresisMs = 5000; + signalThresholdInfoEutran.hysteresisDb = 2; + signalThresholdInfoEutran.thresholds = {-128, -108, -88, -68}; + signalThresholdInfoEutran.isEnabled = true; + signalThresholdInfoEutran.ran = AccessNetwork::EUTRAN; + + SignalThresholdInfo signalThresholdInfoCdma2000; + signalThresholdInfoCdma2000.signalMeasurement = + SignalThresholdInfo::SIGNAL_MEASUREMENT_TYPE_RSSI; + signalThresholdInfoCdma2000.hysteresisMs = 5000; + signalThresholdInfoCdma2000.hysteresisDb = 2; + signalThresholdInfoCdma2000.thresholds = {-105, -90, -75, -65}; + signalThresholdInfoCdma2000.isEnabled = true; + signalThresholdInfoCdma2000.ran = AccessNetwork::CDMA2000; + + SignalThresholdInfo signalThresholdInfoNgran; + signalThresholdInfoNgran.signalMeasurement = + SignalThresholdInfo::SIGNAL_MEASUREMENT_TYPE_SSRSRP; + signalThresholdInfoNgran.hysteresisMs = 5000; + signalThresholdInfoNgran.hysteresisDb = 0; + signalThresholdInfoNgran.thresholds = {-105, -90, -75, -65}; + signalThresholdInfoNgran.isEnabled = true; + signalThresholdInfoNgran.ran = AccessNetwork::NGRAN; + + const static std::vector<SignalThresholdInfo> candidateSignalThresholdInfos = { + signalThresholdInfoGeran, signalThresholdInfoUtran, signalThresholdInfoEutran, + signalThresholdInfoCdma2000, signalThresholdInfoNgran}; + + std::vector<SignalThresholdInfo> supportedSignalThresholdInfos; + for (size_t i = 0; i < candidateSignalThresholdInfos.size(); i++) { + serial = GetRandomSerialNumber(); + ndk::ScopedAStatus res = radio_network->setSignalStrengthReportingCriteria( + serial, {candidateSignalThresholdInfos[i]}); + ASSERT_OK(res); + EXPECT_EQ(std::cv_status::no_timeout, wait()); + if (radioRsp_network->rspInfo.error == RadioError::NONE) { + supportedSignalThresholdInfos.push_back(signalThresholdInfoGeran); + } else { + // Refer to IRadioNetworkResponse#setSignalStrengthReportingCriteriaResponse + ASSERT_TRUE(CheckAnyOfErrors( + radioRsp_network->rspInfo.error, + {RadioError::INVALID_ARGUMENTS, RadioError::RADIO_NOT_AVAILABLE})); + } + } + + ASSERT_FALSE(supportedSignalThresholdInfos.empty()); + + serial = GetRandomSerialNumber(); + ndk::ScopedAStatus res = radio_network->setSignalStrengthReportingCriteria( + serial, supportedSignalThresholdInfos); + ASSERT_OK(res); + EXPECT_EQ(std::cv_status::no_timeout, wait()); + EXPECT_EQ(RadioResponseType::SOLICITED, radioRsp_network->rspInfo.type); + EXPECT_EQ(serial, radioRsp_network->rspInfo.serial); + + ALOGI("setSignalStrengthReportingCriteria_multiRansPerRequest, rspInfo.error = %s\n", + toString(radioRsp_network->rspInfo.error).c_str()); + + ASSERT_TRUE(CheckAnyOfErrors(radioRsp_network->rspInfo.error, {RadioError::NONE})); +} + +/* * Test IRadioNetwork.setLinkCapacityReportingCriteria() invalid hysteresisDlKbps */ TEST_P(RadioNetworkTest, setLinkCapacityReportingCriteria_invalidHysteresisDlKbps) { diff --git a/security/keymint/aidl/android/hardware/security/keymint/IKeyMintOperation.aidl b/security/keymint/aidl/android/hardware/security/keymint/IKeyMintOperation.aidl index ca8955552e..c30c1830af 100644 --- a/security/keymint/aidl/android/hardware/security/keymint/IKeyMintOperation.aidl +++ b/security/keymint/aidl/android/hardware/security/keymint/IKeyMintOperation.aidl @@ -242,7 +242,8 @@ interface IKeyMintOperation { * not a multiple of the AES block size, finish() must return * ErrorCode::INVALID_INPUT_LENGTH. If padding is PaddingMode::PKCS7, pad the data per the * PKCS#7 specification, including adding an additional padding block if the data is a - * multiple of the block length. + * multiple of the block length. If padding is PaddingMode::PKCS7 and decryption does not + * result in valid padding, return ErrorCode::INVALID_ARGUMENT. * * o BlockMode::GCM. During encryption, after processing all plaintext, compute the tag * (Tag::MAC_LENGTH bytes) and append it to the returned ciphertext. During decryption, diff --git a/security/keymint/aidl/vts/functional/KeyMintTest.cpp b/security/keymint/aidl/vts/functional/KeyMintTest.cpp index e73f46c21f..cbe4512ab3 100644 --- a/security/keymint/aidl/vts/functional/KeyMintTest.cpp +++ b/security/keymint/aidl/vts/functional/KeyMintTest.cpp @@ -5481,18 +5481,45 @@ TEST_P(EncryptionOperationsTest, AesEcbPkcs7PaddingCorrupted) { EXPECT_EQ(ErrorCode::OK, Begin(KeyPurpose::DECRYPT, params)); string plaintext; - ErrorCode error = Finish(message, &plaintext); - if (error == ErrorCode::INVALID_INPUT_LENGTH) { + ErrorCode error = Finish(ciphertext, &plaintext); + if (error == ErrorCode::INVALID_ARGUMENT) { // This is the expected error, we can exit the test now. return; } else { // Very small chance we got valid decryption, so try again. - ASSERT_EQ(error, ErrorCode::OK); + ASSERT_EQ(error, ErrorCode::OK) + << "Expected INVALID_ARGUMENT or (rarely) OK, got " << error; } } FAIL() << "Corrupt ciphertext should have failed to decrypt by now."; } +/* + * EncryptionOperationsTest.AesEcbPkcs7CiphertextTooShort + * + * Verifies that AES decryption fails in the correct way when the padding is corrupted. + */ +TEST_P(EncryptionOperationsTest, AesEcbPkcs7CiphertextTooShort) { + ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder() + .Authorization(TAG_NO_AUTH_REQUIRED) + .AesEncryptionKey(128) + .Authorization(TAG_BLOCK_MODE, BlockMode::ECB) + .Padding(PaddingMode::PKCS7))); + + auto params = AuthorizationSetBuilder().BlockMode(BlockMode::ECB).Padding(PaddingMode::PKCS7); + + string message = "a"; + string ciphertext = EncryptMessage(message, params); + EXPECT_EQ(16U, ciphertext.size()); + EXPECT_NE(ciphertext, message); + + // Shorten the ciphertext. + ciphertext.resize(ciphertext.size() - 1); + EXPECT_EQ(ErrorCode::OK, Begin(KeyPurpose::DECRYPT, params)); + string plaintext; + EXPECT_EQ(ErrorCode::INVALID_INPUT_LENGTH, Finish(ciphertext, &plaintext)); +} + vector<uint8_t> CopyIv(const AuthorizationSet& set) { auto iv = set.GetTagValue(TAG_NONCE); EXPECT_TRUE(iv); diff --git a/sensors/aidl/android/hardware/sensors/ISensors.aidl b/sensors/aidl/android/hardware/sensors/ISensors.aidl index 2ac188454b..2c684897cf 100644 --- a/sensors/aidl/android/hardware/sensors/ISensors.aidl +++ b/sensors/aidl/android/hardware/sensors/ISensors.aidl @@ -229,8 +229,7 @@ interface ISensors { * * @param mem shared memory info data structure. * @param out channelHandle The registered channel handle. - * @return The direct channel handle, which is positive if successfully registered, and -1 - * otherwise. + * @return The direct channel handle, which is positive if successfully registered. * @return Status::ok on success * EX_ILLEGAL_ARGUMENT if the shared memory information is not consistent. * EX_UNSUPPORTED_OPERATION if this functionality is unsupported. @@ -245,7 +244,7 @@ interface ISensors { * @see OperationMode * @param mode The operation mode. * @return Status::ok on success - * EX_UNSUPPORTED_OPERATION if requested mode is not supported. + * EX_UNSUPPORTED_OPERATION or EX_ILLEGAL_ARGUMENT if requested mode is not supported. * EX_SECURITY if the operation is not allowed. */ void setOperationMode(in OperationMode mode); diff --git a/sensors/aidl/default/multihal/HalProxyAidl.cpp b/sensors/aidl/default/multihal/HalProxyAidl.cpp index 64805e6638..e6bcdada52 100644 --- a/sensors/aidl/default/multihal/HalProxyAidl.cpp +++ b/sensors/aidl/default/multihal/HalProxyAidl.cpp @@ -29,6 +29,7 @@ using ::aidl::android::hardware::common::fmq::SynchronizedReadWrite; using ::aidl::android::hardware::sensors::ISensors; using ::aidl::android::hardware::sensors::ISensorsCallback; using ::android::hardware::sensors::V2_1::implementation::convertToOldEvent; +using ::ndk::ScopedAStatus; namespace aidl { namespace android { @@ -36,19 +37,22 @@ namespace hardware { namespace sensors { namespace implementation { -static binder_status_t resultToBinderStatus(::android::hardware::sensors::V1_0::Result result) { - switch (result) { - case ::android::hardware::sensors::V1_0::Result::OK: - return STATUS_OK; - case ::android::hardware::sensors::V1_0::Result::PERMISSION_DENIED: - return STATUS_PERMISSION_DENIED; - case ::android::hardware::sensors::V1_0::Result::NO_MEMORY: - return STATUS_NO_MEMORY; - case ::android::hardware::sensors::V1_0::Result::BAD_VALUE: - return STATUS_BAD_VALUE; - case ::android::hardware::sensors::V1_0::Result::INVALID_OPERATION: - return STATUS_INVALID_OPERATION; - } +static ScopedAStatus +resultToAStatus(::android::hardware::sensors::V1_0::Result result) { + switch (result) { + case ::android::hardware::sensors::V1_0::Result::OK: + return ScopedAStatus::ok(); + case ::android::hardware::sensors::V1_0::Result::PERMISSION_DENIED: + return ScopedAStatus::fromExceptionCode(EX_SECURITY); + case ::android::hardware::sensors::V1_0::Result::NO_MEMORY: + return ScopedAStatus::fromServiceSpecificError(ISensors::ERROR_NO_MEMORY); + case ::android::hardware::sensors::V1_0::Result::BAD_VALUE: + return ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT); + case ::android::hardware::sensors::V1_0::Result::INVALID_OPERATION: + return ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION); + default: + return ScopedAStatus::fromExceptionCode(EX_TRANSACTION_FAILED); + } } static ::android::hardware::sensors::V1_0::RateLevel convertRateLevel( @@ -62,6 +66,8 @@ static ::android::hardware::sensors::V1_0::RateLevel convertRateLevel( return ::android::hardware::sensors::V1_0::RateLevel::FAST; case ISensors::RateLevel::VERY_FAST: return ::android::hardware::sensors::V1_0::RateLevel::VERY_FAST; + default: + assert(false); } } @@ -72,6 +78,8 @@ static ::android::hardware::sensors::V1_0::OperationMode convertOperationMode( return ::android::hardware::sensors::V1_0::OperationMode::NORMAL; case ISensors::OperationMode::DATA_INJECTION: return ::android::hardware::sensors::V1_0::OperationMode::DATA_INJECTION; + default: + assert(false); } } @@ -82,6 +90,8 @@ static ::android::hardware::sensors::V1_0::SharedMemType convertSharedMemType( return ::android::hardware::sensors::V1_0::SharedMemType::ASHMEM; case ISensors::SharedMemInfo::SharedMemType::GRALLOC: return ::android::hardware::sensors::V1_0::SharedMemType::GRALLOC; + default: + assert(false); } } @@ -90,6 +100,8 @@ static ::android::hardware::sensors::V1_0::SharedMemFormat convertSharedMemForma switch (sharedMemFormat) { case ISensors::SharedMemInfo::SharedMemFormat::SENSORS_EVENT: return ::android::hardware::sensors::V1_0::SharedMemFormat::SENSORS_EVENT; + default: + assert(false); } } @@ -104,106 +116,125 @@ static ::android::hardware::sensors::V1_0::SharedMemInfo convertSharedMemInfo( return v1SharedMemInfo; } -::ndk::ScopedAStatus HalProxyAidl::activate(int32_t in_sensorHandle, bool in_enabled) { - return ndk::ScopedAStatus::fromStatus( - resultToBinderStatus(HalProxy::activate(in_sensorHandle, in_enabled))); +ScopedAStatus HalProxyAidl::activate(int32_t in_sensorHandle, bool in_enabled) { + return resultToAStatus(HalProxy::activate(in_sensorHandle, in_enabled)); +} + +ScopedAStatus HalProxyAidl::batch(int32_t in_sensorHandle, + int64_t in_samplingPeriodNs, + int64_t in_maxReportLatencyNs) { + return resultToAStatus(HalProxy::batch(in_sensorHandle, in_samplingPeriodNs, + in_maxReportLatencyNs)); } -::ndk::ScopedAStatus HalProxyAidl::batch(int32_t in_sensorHandle, int64_t in_samplingPeriodNs, - int64_t in_maxReportLatencyNs) { - return ndk::ScopedAStatus::fromStatus(resultToBinderStatus( - HalProxy::batch(in_sensorHandle, in_samplingPeriodNs, in_maxReportLatencyNs))); +ScopedAStatus HalProxyAidl::configDirectReport(int32_t in_sensorHandle, + int32_t in_channelHandle, + ISensors::RateLevel in_rate, + int32_t *_aidl_return) { + ScopedAStatus status = + ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION); + HalProxy::configDirectReport( + in_sensorHandle, in_channelHandle, convertRateLevel(in_rate), + [&status, _aidl_return](::android::hardware::sensors::V1_0::Result result, + int32_t reportToken) { + status = resultToAStatus(result); + *_aidl_return = reportToken; + }); + + return status; } -::ndk::ScopedAStatus HalProxyAidl::configDirectReport(int32_t in_sensorHandle, - int32_t in_channelHandle, - ISensors::RateLevel in_rate, - int32_t* _aidl_return) { - binder_status_t binderStatus; - HalProxy::configDirectReport( - in_sensorHandle, in_channelHandle, convertRateLevel(in_rate), - [&binderStatus, _aidl_return](::android::hardware::sensors::V1_0::Result result, - int32_t reportToken) { - binderStatus = resultToBinderStatus(result); - *_aidl_return = reportToken; - }); - return ndk::ScopedAStatus::fromStatus(binderStatus); +ScopedAStatus HalProxyAidl::flush(int32_t in_sensorHandle) { + return resultToAStatus(HalProxy::flush(in_sensorHandle)); } -::ndk::ScopedAStatus HalProxyAidl::flush(int32_t in_sensorHandle) { - return ndk::ScopedAStatus::fromStatus(resultToBinderStatus(HalProxy::flush(in_sensorHandle))); +ScopedAStatus HalProxyAidl::getSensorsList( + std::vector<::aidl::android::hardware::sensors::SensorInfo> *_aidl_return) { + for (const auto &sensor : HalProxy::getSensors()) { + _aidl_return->push_back(convertSensorInfo(sensor.second)); + } + return ScopedAStatus::ok(); } -::ndk::ScopedAStatus HalProxyAidl::getSensorsList( - std::vector<::aidl::android::hardware::sensors::SensorInfo>* _aidl_return) { - for (const auto& sensor : HalProxy::getSensors()) { - _aidl_return->push_back(convertSensorInfo(sensor.second)); - } - return ndk::ScopedAStatus::ok(); -} - -::ndk::ScopedAStatus HalProxyAidl::initialize( - const MQDescriptor<::aidl::android::hardware::sensors::Event, SynchronizedReadWrite>& - in_eventQueueDescriptor, - const MQDescriptor<int32_t, SynchronizedReadWrite>& in_wakeLockDescriptor, - const std::shared_ptr<ISensorsCallback>& in_sensorsCallback) { - ::android::sp<::android::hardware::sensors::V2_1::implementation::ISensorsCallbackWrapperBase> - dynamicCallback = new ISensorsCallbackWrapperAidl(in_sensorsCallback); - - auto aidlEventQueue = - std::make_unique<::android::AidlMessageQueue<::aidl::android::hardware::sensors::Event, - SynchronizedReadWrite>>( - in_eventQueueDescriptor, true /* resetPointers */); - std::unique_ptr< - ::android::hardware::sensors::V2_1::implementation::EventMessageQueueWrapperBase> - eventQueue = std::make_unique<EventMessageQueueWrapperAidl>(aidlEventQueue); - - auto aidlWakeLockQueue = - std::make_unique<::android::AidlMessageQueue<int32_t, SynchronizedReadWrite>>( - in_wakeLockDescriptor, true /* resetPointers */); - std::unique_ptr< - ::android::hardware::sensors::V2_1::implementation::WakeLockMessageQueueWrapperBase> - wakeLockQueue = std::make_unique<WakeLockMessageQueueWrapperAidl>(aidlWakeLockQueue); - - return ndk::ScopedAStatus::fromStatus( - resultToBinderStatus(initializeCommon(eventQueue, wakeLockQueue, dynamicCallback))); -} - -::ndk::ScopedAStatus HalProxyAidl::injectSensorData( - const ::aidl::android::hardware::sensors::Event& in_event) { - ::android::hardware::sensors::V2_1::Event hidlEvent; - convertToHidlEvent(in_event, &hidlEvent); - return ndk::ScopedAStatus::fromStatus( - resultToBinderStatus(HalProxy::injectSensorData(convertToOldEvent(hidlEvent)))); -} - -::ndk::ScopedAStatus HalProxyAidl::registerDirectChannel(const ISensors::SharedMemInfo& in_mem, - int32_t* _aidl_return) { - binder_status_t binderStatus; - ::android::hardware::sensors::V1_0::SharedMemInfo sharedMemInfo = convertSharedMemInfo(in_mem); - - HalProxy::registerDirectChannel( - sharedMemInfo, - [&binderStatus, _aidl_return](::android::hardware::sensors::V1_0::Result result, - int32_t reportToken) { - binderStatus = resultToBinderStatus(result); - *_aidl_return = reportToken; - }); - - native_handle_delete( - const_cast<native_handle_t*>(sharedMemInfo.memoryHandle.getNativeHandle())); - return ndk::ScopedAStatus::fromStatus(binderStatus); -} - -::ndk::ScopedAStatus HalProxyAidl::setOperationMode( - ::aidl::android::hardware::sensors::ISensors::OperationMode in_mode) { - return ndk::ScopedAStatus::fromStatus( - resultToBinderStatus(HalProxy::setOperationMode(convertOperationMode(in_mode)))); -} - -::ndk::ScopedAStatus HalProxyAidl::unregisterDirectChannel(int32_t in_channelHandle) { - return ndk::ScopedAStatus::fromStatus( - resultToBinderStatus(HalProxy::unregisterDirectChannel(in_channelHandle))); +ScopedAStatus HalProxyAidl::initialize( + const MQDescriptor<::aidl::android::hardware::sensors::Event, + SynchronizedReadWrite> &in_eventQueueDescriptor, + const MQDescriptor<int32_t, SynchronizedReadWrite> &in_wakeLockDescriptor, + const std::shared_ptr<ISensorsCallback> &in_sensorsCallback) { + ::android::sp<::android::hardware::sensors::V2_1::implementation:: + ISensorsCallbackWrapperBase> + dynamicCallback = new ISensorsCallbackWrapperAidl(in_sensorsCallback); + + auto aidlEventQueue = std::make_unique<::android::AidlMessageQueue< + ::aidl::android::hardware::sensors::Event, SynchronizedReadWrite>>( + in_eventQueueDescriptor, true /* resetPointers */); + std::unique_ptr<::android::hardware::sensors::V2_1::implementation:: + EventMessageQueueWrapperBase> + eventQueue = + std::make_unique<EventMessageQueueWrapperAidl>(aidlEventQueue); + + auto aidlWakeLockQueue = std::make_unique< + ::android::AidlMessageQueue<int32_t, SynchronizedReadWrite>>( + in_wakeLockDescriptor, true /* resetPointers */); + std::unique_ptr<::android::hardware::sensors::V2_1::implementation:: + WakeLockMessageQueueWrapperBase> + wakeLockQueue = + std::make_unique<WakeLockMessageQueueWrapperAidl>(aidlWakeLockQueue); + + return resultToAStatus( + initializeCommon(eventQueue, wakeLockQueue, dynamicCallback)); +} + +ScopedAStatus HalProxyAidl::injectSensorData( + const ::aidl::android::hardware::sensors::Event &in_event) { + ::android::hardware::sensors::V2_1::Event hidlEvent; + convertToHidlEvent(in_event, &hidlEvent); + return resultToAStatus( + HalProxy::injectSensorData(convertToOldEvent(hidlEvent))); +} + +ScopedAStatus +HalProxyAidl::registerDirectChannel(const ISensors::SharedMemInfo &in_mem, + int32_t *_aidl_return) { + ScopedAStatus status = + ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION); + ::android::hardware::sensors::V1_0::SharedMemInfo sharedMemInfo = + convertSharedMemInfo(in_mem); + + HalProxy::registerDirectChannel( + sharedMemInfo, + [&status, _aidl_return](::android::hardware::sensors::V1_0::Result result, + int32_t reportToken) { + status = resultToAStatus(result); + *_aidl_return = reportToken; + }); + + native_handle_delete(const_cast<native_handle_t *>( + sharedMemInfo.memoryHandle.getNativeHandle())); + + return status; +} + +ScopedAStatus HalProxyAidl::setOperationMode( + ::aidl::android::hardware::sensors::ISensors::OperationMode in_mode) { + return resultToAStatus( + HalProxy::setOperationMode(convertOperationMode(in_mode))); +} + +ScopedAStatus HalProxyAidl::unregisterDirectChannel(int32_t in_channelHandle) { + return resultToAStatus(HalProxy::unregisterDirectChannel(in_channelHandle)); +} + +binder_status_t HalProxyAidl::dump(int fd, const char ** /* args */, + uint32_t /* numArgs */) { + native_handle_t *nativeHandle = + native_handle_create(1 /* numFds */, 0 /* numInts */); + nativeHandle->data[0] = fd; + + HalProxy::debug(nativeHandle, {} /* args */); + + native_handle_delete(nativeHandle); + return STATUS_OK; } } // namespace implementation diff --git a/sensors/aidl/default/multihal/include/HalProxyAidl.h b/sensors/aidl/default/multihal/include/HalProxyAidl.h index 7401726cf9..5c81715933 100644 --- a/sensors/aidl/default/multihal/include/HalProxyAidl.h +++ b/sensors/aidl/default/multihal/include/HalProxyAidl.h @@ -55,6 +55,8 @@ class HalProxyAidl : public ::android::hardware::sensors::V2_1::implementation:: ::ndk::ScopedAStatus setOperationMode( ::aidl::android::hardware::sensors::ISensors::OperationMode in_mode) override; ::ndk::ScopedAStatus unregisterDirectChannel(int32_t in_channelHandle) override; + + binder_status_t dump(int fd, const char **args, uint32_t numArgs) override; }; } // namespace implementation diff --git a/sensors/aidl/vts/VtsAidlHalSensorsTargetTest.cpp b/sensors/aidl/vts/VtsAidlHalSensorsTargetTest.cpp index 83d0dc97e1..d536e290b2 100644 --- a/sensors/aidl/vts/VtsAidlHalSensorsTargetTest.cpp +++ b/sensors/aidl/vts/VtsAidlHalSensorsTargetTest.cpp @@ -599,10 +599,12 @@ TEST_P(SensorsAidlTest, SetOperationMode) { ASSERT_TRUE(getSensors()->setOperationMode(ISensors::OperationMode::DATA_INJECTION).isOk()); ASSERT_TRUE(getSensors()->setOperationMode(ISensors::OperationMode::NORMAL).isOk()); } else { - ASSERT_EQ(getSensors() - ->setOperationMode(ISensors::OperationMode::DATA_INJECTION) - .getExceptionCode(), - EX_UNSUPPORTED_OPERATION); + int errorCode = + getSensors() + ->setOperationMode(ISensors::OperationMode::DATA_INJECTION) + .getExceptionCode(); + ASSERT_TRUE((errorCode == EX_UNSUPPORTED_OPERATION) || + (errorCode == EX_ILLEGAL_ARGUMENT)); } } @@ -938,10 +940,10 @@ void SensorsAidlTest::checkRateLevel(const SensorInfo& sensor, int32_t directCha if (isDirectReportRateSupported(sensor, rateLevel)) { ASSERT_TRUE(status.isOk()); if (rateLevel != ISensors::RateLevel::STOP) { - ASSERT_GT(*reportToken, 0); - } else { - ASSERT_EQ(status.getExceptionCode(), EX_ILLEGAL_ARGUMENT); + ASSERT_GT(*reportToken, 0); } + } else { + ASSERT_EQ(status.getExceptionCode(), EX_ILLEGAL_ARGUMENT); } } @@ -982,11 +984,15 @@ void SensorsAidlTest::verifyRegisterDirectChannel( ::ndk::ScopedAStatus status = registerDirectChannel(mem->getSharedMemInfo(), &channelHandle); if (supportsSharedMemType) { ASSERT_TRUE(status.isOk()); - ASSERT_EQ(channelHandle, 0); + ASSERT_GT(channelHandle, 0); + + // Verify that the memory has been zeroed + for (size_t i = 0; i < mem->getSize(); i++) { + ASSERT_EQ(buffer[i], 0x00); + } } else { int32_t error = supportsAnyDirectChannel ? EX_ILLEGAL_ARGUMENT : EX_UNSUPPORTED_OPERATION; ASSERT_EQ(status.getExceptionCode(), error); - ASSERT_EQ(channelHandle, -1); } *directChannelHandle = channelHandle; } @@ -1038,7 +1044,7 @@ void SensorsAidlTest::verifyConfigure(const SensorInfo& sensor, // Verify that a sensor handle of -1 is only acceptable when using RateLevel::STOP ndk::ScopedAStatus status = configDirectReport(-1 /* sensorHandle */, directChannelHandle, ISensors::RateLevel::NORMAL, &reportToken); - ASSERT_EQ(status.getServiceSpecificError(), android::BAD_VALUE); + ASSERT_EQ(status.getExceptionCode(), EX_ILLEGAL_ARGUMENT); status = configDirectReport(-1 /* sensorHandle */, directChannelHandle, ISensors::RateLevel::STOP, &reportToken); diff --git a/tv/tuner/aidl/vts/functional/VtsHalTvTunerTargetTest.cpp b/tv/tuner/aidl/vts/functional/VtsHalTvTunerTargetTest.cpp index c99da419ed..07e3e3cbe2 100644 --- a/tv/tuner/aidl/vts/functional/VtsHalTvTunerTargetTest.cpp +++ b/tv/tuner/aidl/vts/functional/VtsHalTvTunerTargetTest.cpp @@ -640,6 +640,48 @@ TEST_P(TunerFilterAidlTest, testTimeFilter) { testTimeFilter(timeFilterMap[timeFilter.timeFilterId]); } +static bool isEventProducingFilter(const FilterConfig& filterConfig) { + switch (filterConfig.type.mainType) { + case DemuxFilterMainType::TS: { + auto tsFilterType = + filterConfig.type.subType.get<DemuxFilterSubType::Tag::tsFilterType>(); + return (tsFilterType == DemuxTsFilterType::SECTION || + tsFilterType == DemuxTsFilterType::PES || + tsFilterType == DemuxTsFilterType::AUDIO || + tsFilterType == DemuxTsFilterType::VIDEO || + tsFilterType == DemuxTsFilterType::RECORD || + tsFilterType == DemuxTsFilterType::TEMI); + } + case DemuxFilterMainType::MMTP: { + auto mmtpFilterType = + filterConfig.type.subType.get<DemuxFilterSubType::Tag::mmtpFilterType>(); + return (mmtpFilterType == DemuxMmtpFilterType::SECTION || + mmtpFilterType == DemuxMmtpFilterType::PES || + mmtpFilterType == DemuxMmtpFilterType::AUDIO || + mmtpFilterType == DemuxMmtpFilterType::VIDEO || + mmtpFilterType == DemuxMmtpFilterType::RECORD || + mmtpFilterType == DemuxMmtpFilterType::DOWNLOAD); + } + case DemuxFilterMainType::IP: { + auto ipFilterType = + filterConfig.type.subType.get<DemuxFilterSubType::Tag::ipFilterType>(); + return (ipFilterType == DemuxIpFilterType::SECTION); + } + case DemuxFilterMainType::TLV: { + auto tlvFilterType = + filterConfig.type.subType.get<DemuxFilterSubType::Tag::tlvFilterType>(); + return (tlvFilterType == DemuxTlvFilterType::SECTION); + } + case DemuxFilterMainType::ALP: { + auto alpFilterType = + filterConfig.type.subType.get<DemuxFilterSubType::Tag::alpFilterType>(); + return (alpFilterType == DemuxAlpFilterType::SECTION); + } + default: + return false; + } +} + static bool isMediaFilter(const FilterConfig& filterConfig) { switch (filterConfig.type.mainType) { case DemuxFilterMainType::TS: { @@ -685,6 +727,12 @@ static int getDemuxFilterEventDataLength(const DemuxFilterEvent& event) { // TODO: move boilerplate into text fixture void TunerFilterAidlTest::testDelayHint(const FilterConfig& filterConf) { + if (!filterConf.timeDelayInMs && !filterConf.dataDelayInBytes) { + return; + } + if (!isEventProducingFilter(filterConf)) { + return; + } int32_t feId; int32_t demuxId; std::shared_ptr<IDemux> demux; @@ -707,11 +755,11 @@ void TunerFilterAidlTest::testDelayHint(const FilterConfig& filterConf) { // startTime needs to be set before calling setDelayHint. auto startTime = std::chrono::steady_clock::now(); - auto timeDelayInMs = std::chrono::milliseconds(filterConf.timeDelayInMs); - if (timeDelayInMs.count() > 0) { + int timeDelayInMs = filterConf.timeDelayInMs; + if (timeDelayInMs > 0) { FilterDelayHint delayHint; delayHint.hintType = FilterDelayHintType::TIME_DELAY_IN_MS; - delayHint.hintValue = timeDelayInMs.count(); + delayHint.hintValue = timeDelayInMs; // setDelayHint should fail for media filters. ASSERT_EQ(filter->setDelayHint(delayHint).isOk(), !mediaFilter); @@ -733,6 +781,10 @@ void TunerFilterAidlTest::testDelayHint(const FilterConfig& filterConf) { auto cb = mFilterTests.getFilterCallbacks().at(filterId); auto future = cb->verifyFilterCallback([](const std::vector<DemuxFilterEvent>&) { return true; }); + + // The configure stage can also produce events, so we should set the delay + // hint beforehand. + ASSERT_TRUE(mFilterTests.configFilter(filterConf.settings, filterId)); mFilterTests.startFilter(filterId); auto timeout = std::chrono::seconds(30); @@ -750,20 +802,16 @@ void TunerFilterAidlTest::testDelayHint(const FilterConfig& filterConf) { return true; }); - // The configure stage can also produce events, so we should set the delay - // hint beforehand. - ASSERT_TRUE(mFilterTests.configFilter(filterConf.settings, filterId)); - ASSERT_TRUE(mFilterTests.startFilter(filterId)); // block and wait for callback to be received. ASSERT_EQ(future.wait_for(timeout), std::future_status::ready); - auto duration = std::chrono::steady_clock::now() - startTime; - bool delayHintTest = duration >= timeDelayInMs; + auto duration = std::chrono::steady_clock::now() - startTime; + bool delayHintTest = duration >= std::chrono::milliseconds(timeDelayInMs); bool dataSizeTest = callbackSize >= dataDelayInBytes; - if (timeDelayInMs.count() > 0 && dataDelayInBytes > 0) { + if (timeDelayInMs > 0 && dataDelayInBytes > 0) { ASSERT_TRUE(delayHintTest || dataSizeTest); } else { // if only one of time delay / data delay is configured, one of them @@ -781,7 +829,9 @@ void TunerFilterAidlTest::testDelayHint(const FilterConfig& filterConf) { TEST_P(TunerFilterAidlTest, FilterDelayHintTest) { description("Test filter time delay hint."); - + if (!live.hasFrontendConnection) { + return; + } for (const auto& obj : filterMap) { testDelayHint(obj.second); } diff --git a/tv/tuner/aidl/vts/functional/VtsHalTvTunerTestConfigurations.h b/tv/tuner/aidl/vts/functional/VtsHalTvTunerTestConfigurations.h index 5a72ba45b5..5f1f9c5385 100644 --- a/tv/tuner/aidl/vts/functional/VtsHalTvTunerTestConfigurations.h +++ b/tv/tuner/aidl/vts/functional/VtsHalTvTunerTestConfigurations.h @@ -175,9 +175,14 @@ inline bool validateConnections() { ALOGW("[vts config] Record must support either a DVR source or a Frontend source."); return false; } - bool feIsValid = frontendMap.find(live.frontendId) != frontendMap.end() && - frontendMap.find(scan.frontendId) != frontendMap.end(); - feIsValid &= record.support ? frontendMap.find(record.frontendId) != frontendMap.end() : true; + bool feIsValid = live.hasFrontendConnection + ? frontendMap.find(live.frontendId) != frontendMap.end() + : true; + feIsValid &= scan.hasFrontendConnection ? frontendMap.find(scan.frontendId) != frontendMap.end() + : true; + feIsValid &= record.support && record.hasFrontendConnection + ? frontendMap.find(record.frontendId) != frontendMap.end() + : true; if (!feIsValid) { ALOGW("[vts config] dynamic config fe connection is invalid."); @@ -204,8 +209,10 @@ inline bool validateConnections() { return false; } - bool filterIsValid = filterMap.find(live.audioFilterId) != filterMap.end() && - filterMap.find(live.videoFilterId) != filterMap.end(); + bool filterIsValid = (live.hasFrontendConnection) + ? filterMap.find(live.audioFilterId) != filterMap.end() && + filterMap.find(live.videoFilterId) != filterMap.end() + : true; filterIsValid &= record.support ? filterMap.find(record.recordFilterId) != filterMap.end() : true; diff --git a/tv/tuner/config/TunerTestingConfigAidlReaderV1_0.h b/tv/tuner/config/TunerTestingConfigAidlReaderV1_0.h index 3009c4a28d..189f5fdf77 100644 --- a/tv/tuner/config/TunerTestingConfigAidlReaderV1_0.h +++ b/tv/tuner/config/TunerTestingConfigAidlReaderV1_0.h @@ -867,6 +867,8 @@ struct TunerTestingConfigAidlReader1_0 { settings.set<DemuxFilterSettings::Tag::ip>(ip); break; case FilterSubTypeEnum::IP: { + type.subType.set<DemuxFilterSubType::Tag::ipFilterType>( + DemuxIpFilterType::IP); ip.ipAddr = readIpAddress(filterConfig), ip.filterSettings .set<DemuxIpFilterSettingsFilterSettings::Tag::bPassthrough>( diff --git a/uwb/aidl/Android.bp b/uwb/aidl/Android.bp index 2cc1e6a8f9..36cde9b68d 100755 --- a/uwb/aidl/Android.bp +++ b/uwb/aidl/Android.bp @@ -14,6 +14,7 @@ aidl_interface { vendor_available: true, srcs: ["android/hardware/uwb/*.aidl"], stability: "vintf", + host_supported: true, backend: { java: { sdk_version: "module_Tiramisu", diff --git a/vibrator/aidl/default/Android.bp b/vibrator/aidl/default/Android.bp index acdbdcdd3c..c4140bebc8 100644 --- a/vibrator/aidl/default/Android.bp +++ b/vibrator/aidl/default/Android.bp @@ -63,7 +63,6 @@ cc_fuzz { "libbinder_random_parcel", "libcutils", "liblog", - "libutils", "libvibratorexampleimpl", ], target: { @@ -71,12 +70,14 @@ cc_fuzz { shared_libs: [ "libbinder_ndk", "libbinder", + "libutils", ], }, host: { static_libs: [ "libbinder_ndk", "libbinder", + "libutils", ], }, darwin: { diff --git a/wifi/1.6/default/wifi_legacy_hal.cpp b/wifi/1.6/default/wifi_legacy_hal.cpp index 8a75fd83e3..bb8cf590d9 100644 --- a/wifi/1.6/default/wifi_legacy_hal.cpp +++ b/wifi/1.6/default/wifi_legacy_hal.cpp @@ -1549,13 +1549,14 @@ wifi_error WifiLegacyHal::setIndoorState(bool isIndoor) { std::pair<wifi_error, wifi_radio_combination_matrix*> WifiLegacyHal::getSupportedRadioCombinationsMatrix() { - std::array<char, kMaxSupportedRadioCombinationsMatrixLength> buffer; - buffer.fill(0); + char* buffer = new char[kMaxSupportedRadioCombinationsMatrixLength]; + std::fill(buffer, buffer + kMaxSupportedRadioCombinationsMatrixLength, 0); uint32_t size = 0; wifi_radio_combination_matrix* radio_combination_matrix_ptr = - reinterpret_cast<wifi_radio_combination_matrix*>(buffer.data()); + reinterpret_cast<wifi_radio_combination_matrix*>(buffer); wifi_error status = global_func_table_.wifi_get_supported_radio_combinations_matrix( - global_handle_, buffer.size(), &size, radio_combination_matrix_ptr); + global_handle_, kMaxSupportedRadioCombinationsMatrixLength, &size, + radio_combination_matrix_ptr); CHECK(size >= 0 && size <= kMaxSupportedRadioCombinationsMatrixLength); return {status, radio_combination_matrix_ptr}; } |