Merge "GnssStatus for GnssMeasurement-only VTS test" into udc-dev
diff --git a/audio/aidl/android/hardware/audio/core/IModule.aidl b/audio/aidl/android/hardware/audio/core/IModule.aidl
index 7830501..7622d9a 100644
--- a/audio/aidl/android/hardware/audio/core/IModule.aidl
+++ b/audio/aidl/android/hardware/audio/core/IModule.aidl
@@ -192,6 +192,19 @@
* device address is specified for a point-to-multipoint external device
* connection.
*
+ * Since not all modules have a DSP that could perform sample rate and
+ * format conversions, behavior related to mix port configurations may vary.
+ * For modules with a DSP, mix ports can be pre-configured and have a fixed
+ * set of audio profiles supported by the DSP. For modules without a DSP,
+ * audio profiles of mix ports may change after connecting an external
+ * device. The typical case is that the mix port has an empty set of
+ * profiles when no external devices are connected, and after external
+ * device connection it receives the same set of profiles as the device
+ * ports that they can be routed to. The client will re-query current port
+ * configurations using 'getAudioPorts'. All mix ports that can be routed to
+ * the connected device port must have a non-empty set of audio profiles
+ * after successful connection of an external device.
+ *
* Handling of a disconnect is done in a reverse order:
* 1. Reset port configuration using the 'resetAudioPortConfig' method.
* 2. Release the connected device port by calling the 'disconnectExternalDevice'
diff --git a/audio/aidl/common/Android.bp b/audio/aidl/common/Android.bp
index a3f7f0b..4c6a74e 100644
--- a/audio/aidl/common/Android.bp
+++ b/audio/aidl/common/Android.bp
@@ -41,6 +41,20 @@
],
}
+cc_library {
+ name: "libaudioaidlranges",
+ host_supported: true,
+ vendor_available: true,
+ static_libs: [
+ "android.hardware.audio.effect-V1-ndk",
+ ],
+ export_include_dirs: ["include"],
+ header_libs: ["libaudioaidl_headers"],
+ srcs: [
+ "EffectRangeSpecific.cpp",
+ ],
+}
+
cc_test {
name: "libaudioaidlcommon_test",
host_supported: true,
diff --git a/audio/aidl/common/EffectRangeSpecific.cpp b/audio/aidl/common/EffectRangeSpecific.cpp
new file mode 100644
index 0000000..bd78ea0
--- /dev/null
+++ b/audio/aidl/common/EffectRangeSpecific.cpp
@@ -0,0 +1,161 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <aidl/android/hardware/audio/effect/DynamicsProcessing.h>
+#include <aidl/android/hardware/audio/effect/Range.h>
+
+#include "EffectRangeSpecific.h"
+#include "effect-impl/EffectRange.h"
+
+namespace aidl::android::hardware::audio::effect {
+
+namespace DynamicsProcessingRanges {
+
+static bool isInputGainConfigInRange(const std::vector<DynamicsProcessing::InputGain>& cfgs,
+ const DynamicsProcessing::InputGain& min,
+ const DynamicsProcessing::InputGain& max) {
+ auto func = [](const DynamicsProcessing::InputGain& arg) {
+ return std::make_tuple(arg.channel, arg.gainDb);
+ };
+ return isTupleInRange(cfgs, min, max, func);
+}
+
+static bool isLimiterConfigInRange(const std::vector<DynamicsProcessing::LimiterConfig>& cfgs,
+ const DynamicsProcessing::LimiterConfig& min,
+ const DynamicsProcessing::LimiterConfig& max) {
+ auto func = [](const DynamicsProcessing::LimiterConfig& arg) {
+ return std::make_tuple(arg.channel, arg.enable, arg.linkGroup, arg.attackTimeMs,
+ arg.releaseTimeMs, arg.ratio, arg.thresholdDb, arg.postGainDb);
+ };
+ return isTupleInRange(cfgs, min, max, func);
+}
+
+static bool isMbcBandConfigInRange(const std::vector<DynamicsProcessing::MbcBandConfig>& cfgs,
+ const DynamicsProcessing::MbcBandConfig& min,
+ const DynamicsProcessing::MbcBandConfig& max) {
+ auto func = [](const DynamicsProcessing::MbcBandConfig& arg) {
+ return std::make_tuple(arg.channel, arg.band, arg.enable, arg.cutoffFrequencyHz,
+ arg.attackTimeMs, arg.releaseTimeMs, arg.ratio, arg.thresholdDb,
+ arg.kneeWidthDb, arg.noiseGateThresholdDb, arg.expanderRatio,
+ arg.preGainDb, arg.postGainDb);
+ };
+ return isTupleInRange(cfgs, min, max, func);
+}
+
+static bool isEqBandConfigInRange(const std::vector<DynamicsProcessing::EqBandConfig>& cfgs,
+ const DynamicsProcessing::EqBandConfig& min,
+ const DynamicsProcessing::EqBandConfig& max) {
+ auto func = [](const DynamicsProcessing::EqBandConfig& arg) {
+ return std::make_tuple(arg.channel, arg.band, arg.enable, arg.cutoffFrequencyHz,
+ arg.gainDb);
+ };
+ return isTupleInRange(cfgs, min, max, func);
+}
+
+static bool isChannelConfigInRange(const std::vector<DynamicsProcessing::ChannelConfig>& cfgs,
+ const DynamicsProcessing::ChannelConfig& min,
+ const DynamicsProcessing::ChannelConfig& max) {
+ auto func = [](const DynamicsProcessing::ChannelConfig& arg) {
+ return std::make_tuple(arg.channel, arg.enable);
+ };
+ return isTupleInRange(cfgs, min, max, func);
+}
+
+static bool isEngineConfigInRange(const DynamicsProcessing::EngineArchitecture& cfg,
+ const DynamicsProcessing::EngineArchitecture& min,
+ const DynamicsProcessing::EngineArchitecture& max) {
+ auto func = [](const DynamicsProcessing::EngineArchitecture& arg) {
+ return std::make_tuple(arg.resolutionPreference, arg.preferredProcessingDurationMs,
+ arg.preEqStage.inUse, arg.preEqStage.bandCount,
+ arg.postEqStage.inUse, arg.postEqStage.bandCount, arg.mbcStage.inUse,
+ arg.mbcStage.bandCount, arg.limiterInUse);
+ };
+ return isTupleInRange(func(cfg), func(min), func(max));
+}
+
+static int locateMinMaxForTag(DynamicsProcessing::Tag tag,
+ const std::vector<Range::DynamicsProcessingRange>& ranges) {
+ for (int i = 0; i < (int)ranges.size(); i++) {
+ if (tag == ranges[i].min.getTag() && tag == ranges[i].max.getTag()) {
+ return i;
+ }
+ }
+ return -1;
+}
+
+bool isParamInRange(const DynamicsProcessing& dp,
+ const std::vector<Range::DynamicsProcessingRange>& ranges) {
+ auto tag = dp.getTag();
+ int i = locateMinMaxForTag(tag, ranges);
+ if (i == -1) return true;
+
+ switch (tag) {
+ case DynamicsProcessing::engineArchitecture: {
+ return isEngineConfigInRange(
+ dp.get<DynamicsProcessing::engineArchitecture>(),
+ ranges[i].min.get<DynamicsProcessing::engineArchitecture>(),
+ ranges[i].max.get<DynamicsProcessing::engineArchitecture>());
+ }
+ case DynamicsProcessing::preEq: {
+ return isChannelConfigInRange(dp.get<DynamicsProcessing::preEq>(),
+ ranges[i].min.get<DynamicsProcessing::preEq>()[0],
+ ranges[i].max.get<DynamicsProcessing::preEq>()[0]);
+ }
+ case DynamicsProcessing::postEq: {
+ return isChannelConfigInRange(dp.get<DynamicsProcessing::postEq>(),
+ ranges[i].min.get<DynamicsProcessing::postEq>()[0],
+ ranges[i].max.get<DynamicsProcessing::postEq>()[0]);
+ }
+ case DynamicsProcessing::mbc: {
+ return isChannelConfigInRange(dp.get<DynamicsProcessing::mbc>(),
+ ranges[i].min.get<DynamicsProcessing::mbc>()[0],
+ ranges[i].max.get<DynamicsProcessing::mbc>()[0]);
+ }
+ case DynamicsProcessing::preEqBand: {
+ return isEqBandConfigInRange(dp.get<DynamicsProcessing::preEqBand>(),
+ ranges[i].min.get<DynamicsProcessing::preEqBand>()[0],
+ ranges[i].max.get<DynamicsProcessing::preEqBand>()[0]);
+ }
+ case DynamicsProcessing::postEqBand: {
+ return isEqBandConfigInRange(dp.get<DynamicsProcessing::postEqBand>(),
+ ranges[i].min.get<DynamicsProcessing::postEqBand>()[0],
+ ranges[i].max.get<DynamicsProcessing::postEqBand>()[0]);
+ }
+ case DynamicsProcessing::mbcBand: {
+ return isMbcBandConfigInRange(dp.get<DynamicsProcessing::mbcBand>(),
+ ranges[i].min.get<DynamicsProcessing::mbcBand>()[0],
+ ranges[i].max.get<DynamicsProcessing::mbcBand>()[0]);
+ }
+ case DynamicsProcessing::limiter: {
+ return isLimiterConfigInRange(dp.get<DynamicsProcessing::limiter>(),
+ ranges[i].min.get<DynamicsProcessing::limiter>()[0],
+ ranges[i].max.get<DynamicsProcessing::limiter>()[0]);
+ }
+ case DynamicsProcessing::inputGain: {
+ return isInputGainConfigInRange(dp.get<DynamicsProcessing::inputGain>(),
+ ranges[i].min.get<DynamicsProcessing::inputGain>()[0],
+ ranges[i].max.get<DynamicsProcessing::inputGain>()[0]);
+ }
+ default: {
+ return true;
+ }
+ }
+ return true;
+}
+
+} // namespace DynamicsProcessingRanges
+
+} // namespace aidl::android::hardware::audio::effect
\ No newline at end of file
diff --git a/audio/aidl/common/include/EffectRangeSpecific.h b/audio/aidl/common/include/EffectRangeSpecific.h
new file mode 100644
index 0000000..c7262bb
--- /dev/null
+++ b/audio/aidl/common/include/EffectRangeSpecific.h
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+namespace aidl::android::hardware::audio::effect {
+
+namespace DynamicsProcessingRanges {
+
+bool isParamInRange(const DynamicsProcessing& dp,
+ const std::vector<Range::DynamicsProcessingRange>& ranges);
+
+} // namespace DynamicsProcessingRanges
+
+} // namespace aidl::android::hardware::audio::effect
\ No newline at end of file
diff --git a/audio/aidl/common/include/Utils.h b/audio/aidl/common/include/Utils.h
index 2cf862c..305c924 100644
--- a/audio/aidl/common/include/Utils.h
+++ b/audio/aidl/common/include/Utils.h
@@ -99,6 +99,12 @@
return 0;
}
+constexpr bool isDefaultAudioFormat(
+ const ::aidl::android::media::audio::common::AudioFormatDescription& desc) {
+ return desc.type == ::aidl::android::media::audio::common::AudioFormatType::DEFAULT &&
+ desc.pcm == ::aidl::android::media::audio::common::PcmType::DEFAULT;
+}
+
constexpr bool isTelephonyDeviceType(
::aidl::android::media::audio::common::AudioDeviceType device) {
return device == ::aidl::android::media::audio::common::AudioDeviceType::IN_TELEPHONY_RX ||
diff --git a/audio/aidl/default/EffectFactory.cpp b/audio/aidl/default/EffectFactory.cpp
index c1ac3f2..f0687cc 100644
--- a/audio/aidl/default/EffectFactory.cpp
+++ b/audio/aidl/default/EffectFactory.cpp
@@ -75,6 +75,8 @@
RETURN_IF(!libInterface || !libInterface->queryEffectFunc, EX_NULL_POINTER,
"dlNullQueryEffectFunc");
RETURN_IF_BINDER_EXCEPTION(libInterface->queryEffectFunc(&id.uuid, &desc));
+ // update proxy UUID with information from config xml
+ desc.common.id.proxy = id.proxy;
_aidl_return->emplace_back(std::move(desc));
}
}
diff --git a/audio/aidl/default/Module.cpp b/audio/aidl/default/Module.cpp
index 984b9a1..6b417a4 100644
--- a/audio/aidl/default/Module.cpp
+++ b/audio/aidl/default/Module.cpp
@@ -143,6 +143,21 @@
}
}
+std::ostream& operator<<(std::ostream& os, Module::Type t) {
+ switch (t) {
+ case Module::Type::DEFAULT:
+ os << "default";
+ break;
+ case Module::Type::R_SUBMIX:
+ os << "r_submix";
+ break;
+ case Module::Type::USB:
+ os << "usb";
+ break;
+ }
+ return os;
+}
+
void Module::cleanUpPatch(int32_t patchId) {
erase_all_values(mPatches, std::set<int32_t>{patchId});
}
@@ -352,16 +367,17 @@
ndk::ScopedAStatus Module::setModuleDebug(
const ::aidl::android::hardware::audio::core::ModuleDebug& in_debug) {
- LOG(DEBUG) << __func__ << ": old flags:" << mDebug.toString()
+ LOG(DEBUG) << __func__ << ": " << mType << ": old flags:" << mDebug.toString()
<< ", new flags: " << in_debug.toString();
if (mDebug.simulateDeviceConnections != in_debug.simulateDeviceConnections &&
!mConnectedDevicePorts.empty()) {
- LOG(ERROR) << __func__ << ": attempting to change device connections simulation "
- << "while having external devices connected";
+ LOG(ERROR) << __func__ << ": " << mType
+ << ": attempting to change device connections simulation while having external "
+ << "devices connected";
return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
}
if (in_debug.streamTransientStateDelayMs < 0) {
- LOG(ERROR) << __func__ << ": streamTransientStateDelayMs is negative: "
+ LOG(ERROR) << __func__ << ": " << mType << ": streamTransientStateDelayMs is negative: "
<< in_debug.streamTransientStateDelayMs;
return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
}
@@ -440,38 +456,45 @@
LOG(DEBUG) << __func__ << ": device port " << connectedPort.id << " device set to "
<< connectedDevicePort.device.toString();
// Check if there is already a connected port with for the same external device.
- for (auto connectedPortId : mConnectedDevicePorts) {
- auto connectedPortIt = findById<AudioPort>(ports, connectedPortId);
+ for (auto connectedPortPair : mConnectedDevicePorts) {
+ auto connectedPortIt = findById<AudioPort>(ports, connectedPortPair.first);
if (connectedPortIt->ext.get<AudioPortExt::Tag::device>().device ==
connectedDevicePort.device) {
LOG(ERROR) << __func__ << ": device " << connectedDevicePort.device.toString()
- << " is already connected at the device port id " << connectedPortId;
+ << " is already connected at the device port id "
+ << connectedPortPair.first;
return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
}
}
}
if (!mDebug.simulateDeviceConnections) {
- // In a real HAL here we would attempt querying the profiles from the device.
- LOG(ERROR) << __func__ << ": failed to query supported device profiles";
- // TODO: Check the return value when it is ready for actual devices.
- populateConnectedDevicePort(&connectedPort);
+ if (ndk::ScopedAStatus status = populateConnectedDevicePort(&connectedPort);
+ !status.isOk()) {
+ return status;
+ }
+ } else {
+ auto& connectedProfiles = getConfig().connectedProfiles;
+ if (auto connectedProfilesIt = connectedProfiles.find(templateId);
+ connectedProfilesIt != connectedProfiles.end()) {
+ connectedPort.profiles = connectedProfilesIt->second;
+ }
+ }
+ if (connectedPort.profiles.empty()) {
+ LOG(ERROR) << "Profiles of a connected port still empty after connecting external device "
+ << connectedPort.toString();
return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
}
connectedPort.id = ++getConfig().nextPortId;
- mConnectedDevicePorts.insert(connectedPort.id);
+ auto [connectedPortsIt, _] =
+ mConnectedDevicePorts.insert(std::pair(connectedPort.id, std::vector<int32_t>()));
LOG(DEBUG) << __func__ << ": template port " << templateId << " external device connected, "
<< "connected port ID " << connectedPort.id;
- auto& connectedProfiles = getConfig().connectedProfiles;
- if (auto connectedProfilesIt = connectedProfiles.find(templateId);
- connectedProfilesIt != connectedProfiles.end()) {
- connectedPort.profiles = connectedProfilesIt->second;
- }
ports.push_back(connectedPort);
onExternalDeviceConnectionChanged(connectedPort, true /*connected*/);
- *_aidl_return = std::move(connectedPort);
+ std::vector<int32_t> routablePortIds;
std::vector<AudioRoute> newRoutes;
auto& routes = getConfig().routes;
for (auto& r : routes) {
@@ -481,15 +504,30 @@
newRoute.sinkPortId = connectedPort.id;
newRoute.isExclusive = r.isExclusive;
newRoutes.push_back(std::move(newRoute));
+ routablePortIds.insert(routablePortIds.end(), r.sourcePortIds.begin(),
+ r.sourcePortIds.end());
} else {
auto& srcs = r.sourcePortIds;
if (std::find(srcs.begin(), srcs.end(), templateId) != srcs.end()) {
srcs.push_back(connectedPort.id);
+ routablePortIds.push_back(r.sinkPortId);
}
}
}
routes.insert(routes.end(), newRoutes.begin(), newRoutes.end());
+ // Note: this is a simplistic approach assuming that a mix port can only be populated
+ // from a single device port. Implementing support for stuffing dynamic profiles with a superset
+ // of all profiles from all routable dynamic device ports would be more involved.
+ for (const auto mixPortId : routablePortIds) {
+ auto portsIt = findById<AudioPort>(ports, mixPortId);
+ if (portsIt != ports.end() && portsIt->profiles.empty()) {
+ portsIt->profiles = connectedPort.profiles;
+ connectedPortsIt->second.push_back(portsIt->id);
+ }
+ }
+ *_aidl_return = std::move(connectedPort);
+
return ndk::ScopedAStatus::ok();
}
@@ -504,7 +542,8 @@
LOG(ERROR) << __func__ << ": port id " << in_portId << " is not a device port";
return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
}
- if (mConnectedDevicePorts.count(in_portId) == 0) {
+ auto connectedPortsIt = mConnectedDevicePorts.find(in_portId);
+ if (connectedPortsIt == mConnectedDevicePorts.end()) {
LOG(ERROR) << __func__ << ": port id " << in_portId << " is not a connected device port";
return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
}
@@ -525,7 +564,6 @@
}
onExternalDeviceConnectionChanged(*portIt, false /*connected*/);
ports.erase(portIt);
- mConnectedDevicePorts.erase(in_portId);
LOG(DEBUG) << __func__ << ": connected device port " << in_portId << " released";
auto& routes = getConfig().routes;
@@ -540,6 +578,14 @@
}
}
+ for (const auto mixPortId : connectedPortsIt->second) {
+ auto mixPortIt = findById<AudioPort>(ports, mixPortId);
+ if (mixPortIt != ports.end()) {
+ mixPortIt->profiles = {};
+ }
+ }
+ mConnectedDevicePorts.erase(connectedPortsIt);
+
return ndk::ScopedAStatus::ok();
}
diff --git a/audio/aidl/default/include/core-impl/Configuration.h b/audio/aidl/default/include/core-impl/Configuration.h
index 4dd0133..70320e4 100644
--- a/audio/aidl/default/include/core-impl/Configuration.h
+++ b/audio/aidl/default/include/core-impl/Configuration.h
@@ -33,7 +33,8 @@
std::vector<::aidl::android::media::audio::common::AudioPort> ports;
std::vector<::aidl::android::media::audio::common::AudioPortConfig> portConfigs;
std::vector<::aidl::android::media::audio::common::AudioPortConfig> initialConfigs;
- // Port id -> List of profiles to use when the device port state is set to 'connected'.
+ // Port id -> List of profiles to use when the device port state is set to 'connected'
+ // in connection simulation mode.
std::map<int32_t, std::vector<::aidl::android::media::audio::common::AudioProfile>>
connectedProfiles;
std::vector<AudioRoute> routes;
diff --git a/audio/aidl/default/include/core-impl/Module.h b/audio/aidl/default/include/core-impl/Module.h
index 2cbda7d..83ecfaa 100644
--- a/audio/aidl/default/include/core-impl/Module.h
+++ b/audio/aidl/default/include/core-impl/Module.h
@@ -177,8 +177,10 @@
ChildInterface<IBluetooth> mBluetooth;
ChildInterface<IBluetoothA2dp> mBluetoothA2dp;
ChildInterface<IBluetoothLe> mBluetoothLe;
- // ids of ports created at runtime via 'connectExternalDevice'.
- std::set<int32_t> mConnectedDevicePorts;
+ // ids of device ports created at runtime via 'connectExternalDevice'.
+ // Also stores ids of mix ports with dynamic profiles which got populated from the connected
+ // port.
+ std::map<int32_t, std::vector<int32_t>> mConnectedDevicePorts;
Streams mStreams;
// Maps port ids and port config ids to patch ids.
// Multimap because both ports and configs can be used by multiple patches.
diff --git a/audio/aidl/default/include/core-impl/StreamUsb.h b/audio/aidl/default/include/core-impl/StreamUsb.h
index f1815dd..36e64cb 100644
--- a/audio/aidl/default/include/core-impl/StreamUsb.h
+++ b/audio/aidl/default/include/core-impl/StreamUsb.h
@@ -56,7 +56,7 @@
std::vector<::aidl::android::media::audio::common::AudioDeviceAddress> mConnectedDevices
GUARDED_BY(mLock);
std::vector<std::shared_ptr<alsa_device_proxy>> mAlsaDeviceProxies GUARDED_BY(mLock);
- bool mIsStandby = false;
+ bool mIsStandby = true;
};
class StreamInUsb final : public StreamIn {
diff --git a/audio/aidl/default/include/effect-impl/EffectRange.h b/audio/aidl/default/include/effect-impl/EffectRange.h
new file mode 100644
index 0000000..a3ea01f
--- /dev/null
+++ b/audio/aidl/default/include/effect-impl/EffectRange.h
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <algorithm>
+#include <tuple>
+#include <utility>
+#include <vector>
+
+namespace aidl::android::hardware::audio::effect {
+
+template <typename T>
+bool isInRange(const T& value, const T& low, const T& high) {
+ return (value >= low) && (value <= high);
+}
+
+template <typename T, std::size_t... Is>
+bool isTupleInRange(const T& test, const T& min, const T& max, std::index_sequence<Is...>) {
+ return (isInRange(std::get<Is>(test), std::get<Is>(min), std::get<Is>(max)) && ...);
+}
+
+template <typename T, std::size_t TupSize = std::tuple_size_v<T>>
+bool isTupleInRange(const T& test, const T& min, const T& max) {
+ return isTupleInRange(test, min, max, std::make_index_sequence<TupSize>{});
+}
+
+template <typename T, typename F>
+bool isTupleInRange(const std::vector<T>& cfgs, const T& min, const T& max, const F& func) {
+ auto minT = func(min), maxT = func(max);
+ return std::all_of(cfgs.cbegin(), cfgs.cend(),
+ [&](const T& cfg) { return isTupleInRange(func(cfg), minT, maxT); });
+}
+
+} // namespace aidl::android::hardware::audio::effect
diff --git a/audio/aidl/default/usb/ModuleUsb.cpp b/audio/aidl/default/usb/ModuleUsb.cpp
index 80b0a5b..89895bf 100644
--- a/audio/aidl/default/usb/ModuleUsb.cpp
+++ b/audio/aidl/default/usb/ModuleUsb.cpp
@@ -120,7 +120,11 @@
const bool isInput = isUsbInputDeviceType(devicePort.device.type.type);
alsa_device_profile profile;
profile_init(&profile, isInput ? PCM_IN : PCM_OUT);
+ profile.card = alsaAddress[0];
+ profile.device = alsaAddress[1];
if (!profile_read_device_info(&profile)) {
+ LOG(ERROR) << __func__ << ": failed to read device info, card=" << profile.card
+ << ", device=" << profile.device;
return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
}
diff --git a/audio/aidl/default/usb/StreamUsb.cpp b/audio/aidl/default/usb/StreamUsb.cpp
index fbfe0f1..5d1d7fe 100644
--- a/audio/aidl/default/usb/StreamUsb.cpp
+++ b/audio/aidl/default/usb/StreamUsb.cpp
@@ -107,10 +107,13 @@
::android::status_t DriverUsb::transfer(void* buffer, size_t frameCount, size_t* actualFrameCount,
int32_t* latencyMs) {
if (!mConfig.has_value() || mConnectedDevices.empty()) {
+ LOG(ERROR) << __func__ << ": failed, has config: " << mConfig.has_value()
+ << ", has connected devices: " << mConnectedDevices.empty();
return ::android::NO_INIT;
}
if (mIsStandby) {
if (::android::status_t status = exitStandby(); status != ::android::OK) {
+ LOG(ERROR) << __func__ << ": failed to exit standby, status=" << status;
return status;
}
}
@@ -151,6 +154,7 @@
std::vector<std::shared_ptr<alsa_device_proxy>> alsaDeviceProxies;
for (const auto& device : connectedDevices) {
alsa_device_profile profile;
+ profile_init(&profile, mIsInput ? PCM_IN : PCM_OUT);
profile.card = device.get<AudioDeviceAddress::alsa>()[0];
profile.device = device.get<AudioDeviceAddress::alsa>()[1];
if (!profile_read_device_info(&profile)) {
@@ -174,6 +178,11 @@
<< " error=" << err;
return ::android::UNKNOWN_ERROR;
}
+ if (int err = proxy_open(proxy.get()); err != 0) {
+ LOG(ERROR) << __func__ << ": failed to open device, address=" << device.toString()
+ << " error=" << err;
+ return ::android::UNKNOWN_ERROR;
+ }
alsaDeviceProxies.push_back(std::move(proxy));
}
{
diff --git a/audio/aidl/default/usb/UsbAlsaUtils.cpp b/audio/aidl/default/usb/UsbAlsaUtils.cpp
index 3a74c2a..74d9c28 100644
--- a/audio/aidl/default/usb/UsbAlsaUtils.cpp
+++ b/audio/aidl/default/usb/UsbAlsaUtils.cpp
@@ -114,8 +114,8 @@
static const AudioFormatDescToPcmFormatMap formatDescToPcmFormatMap = {
{make_AudioFormatDescription(PcmType::UINT_8_BIT), PCM_FORMAT_S8},
{make_AudioFormatDescription(PcmType::INT_16_BIT), PCM_FORMAT_S16_LE},
- {make_AudioFormatDescription(PcmType::INT_24_BIT), PCM_FORMAT_S24_LE},
- {make_AudioFormatDescription(PcmType::FIXED_Q_8_24), PCM_FORMAT_S24_3LE},
+ {make_AudioFormatDescription(PcmType::FIXED_Q_8_24), PCM_FORMAT_S24_LE},
+ {make_AudioFormatDescription(PcmType::INT_24_BIT), PCM_FORMAT_S24_3LE},
{make_AudioFormatDescription(PcmType::INT_32_BIT), PCM_FORMAT_S32_LE},
{make_AudioFormatDescription(PcmType::FLOAT_32_BIT), PCM_FORMAT_FLOAT_LE},
};
diff --git a/audio/aidl/sounddose/vts/VtsHalSoundDoseFactoryTargetTest.cpp b/audio/aidl/sounddose/vts/VtsHalSoundDoseFactoryTargetTest.cpp
index df35bae..7722ccf 100644
--- a/audio/aidl/sounddose/vts/VtsHalSoundDoseFactoryTargetTest.cpp
+++ b/audio/aidl/sounddose/vts/VtsHalSoundDoseFactoryTargetTest.cpp
@@ -71,6 +71,7 @@
std::shared_ptr<ISoundDoseFactory> soundDoseFactory;
};
+// @VsrTest = VSR-5.5-002.001
TEST_P(SoundDoseFactory, GetSoundDoseForSameModule) {
const std::string module = "primary";
diff --git a/audio/aidl/vts/Android.bp b/audio/aidl/vts/Android.bp
index 18aa6f0..852255d 100644
--- a/audio/aidl/vts/Android.bp
+++ b/audio/aidl/vts/Android.bp
@@ -84,6 +84,7 @@
cc_test {
name: "VtsHalDynamicsProcessingTargetTest",
defaults: ["VtsHalAudioTargetTestDefaults"],
+ static_libs: ["libaudioaidlranges"],
srcs: ["VtsHalDynamicsProcessingTest.cpp"],
}
diff --git a/audio/aidl/vts/EffectHelper.h b/audio/aidl/vts/EffectHelper.h
index f6683cc..831977b 100644
--- a/audio/aidl/vts/EffectHelper.h
+++ b/audio/aidl/vts/EffectHelper.h
@@ -228,10 +228,10 @@
*/
template <typename S, typename = std::enable_if_t<std::is_arithmetic_v<S>>>
static std::set<S> expandTestValueBasic(std::set<S>& s) {
- const auto min = *s.begin(), max = *s.rbegin();
const auto minLimit = std::numeric_limits<S>::min(),
maxLimit = std::numeric_limits<S>::max();
if (s.size()) {
+ const auto min = *s.begin(), max = *s.rbegin();
s.insert(min + (max - min) / 2);
if (min != minLimit) {
s.insert(min - 1);
diff --git a/audio/aidl/vts/VtsHalAudioCoreModuleTargetTest.cpp b/audio/aidl/vts/VtsHalAudioCoreModuleTargetTest.cpp
index e790d4f..825b865 100644
--- a/audio/aidl/vts/VtsHalAudioCoreModuleTargetTest.cpp
+++ b/audio/aidl/vts/VtsHalAudioCoreModuleTargetTest.cpp
@@ -129,7 +129,9 @@
using Tag = AudioDeviceAddress::Tag;
if (std::string_view connection = description.connection;
connection == AudioDeviceDescription::CONNECTION_BT_A2DP ||
- connection == AudioDeviceDescription::CONNECTION_BT_LE ||
+ // Note: BT LE Broadcast uses a "group id".
+ (description.type != AudioDeviceType::OUT_BROADCAST &&
+ connection == AudioDeviceDescription::CONNECTION_BT_LE) ||
connection == AudioDeviceDescription::CONNECTION_BT_SCO ||
connection == AudioDeviceDescription::CONNECTION_WIRELESS) {
return Tag::mac;
@@ -1778,6 +1780,42 @@
}
}
+// Note: This test relies on simulation of external device connections by the HAL module.
+TEST_P(AudioCoreModule, ExternalDeviceMixPortConfigs) {
+ // After an external device has been connected, all mix ports that can be routed
+ // to the device port for the connected device must have non-empty profiles.
+ ASSERT_NO_FATAL_FAILURE(SetUpModuleConfig());
+ std::vector<AudioPort> externalDevicePorts = moduleConfig->getExternalDevicePorts();
+ if (externalDevicePorts.empty()) {
+ GTEST_SKIP() << "No external devices in the module.";
+ }
+ for (const auto& port : externalDevicePorts) {
+ WithDevicePortConnectedState portConnected(GenerateUniqueDeviceAddress(port));
+ ASSERT_NO_FATAL_FAILURE(portConnected.SetUp(module.get()));
+ std::vector<AudioRoute> routes;
+ ASSERT_IS_OK(module->getAudioRoutesForAudioPort(portConnected.getId(), &routes));
+ std::vector<AudioPort> allPorts;
+ ASSERT_IS_OK(module->getAudioPorts(&allPorts));
+ for (const auto& r : routes) {
+ if (r.sinkPortId == portConnected.getId()) {
+ for (const auto& srcPortId : r.sourcePortIds) {
+ const auto srcPortIt = findById(allPorts, srcPortId);
+ ASSERT_NE(allPorts.end(), srcPortIt) << "port ID " << srcPortId;
+ EXPECT_NE(0UL, srcPortIt->profiles.size())
+ << " source port " << srcPortIt->toString() << " must have its profiles"
+ << " populated following external device connection";
+ }
+ } else {
+ const auto sinkPortIt = findById(allPorts, r.sinkPortId);
+ ASSERT_NE(allPorts.end(), sinkPortIt) << "port ID " << r.sinkPortId;
+ EXPECT_NE(0UL, sinkPortIt->profiles.size())
+ << " source port " << sinkPortIt->toString() << " must have its"
+ << " profiles populated following external device connection";
+ }
+ }
+ }
+}
+
TEST_P(AudioCoreModule, MasterMute) {
bool isSupported = false;
EXPECT_NO_FATAL_FAILURE(TestAccessors<bool>(module.get(), &IModule::getMasterMute,
@@ -3532,6 +3570,7 @@
return ndk::ScopedAStatus::ok();
}
+// @VsrTest = VSR-5.5-002.001
TEST_P(AudioCoreSoundDose, SameInstance) {
if (soundDose == nullptr) {
GTEST_SKIP() << "SoundDose is not supported";
@@ -3543,6 +3582,7 @@
<< "getSoundDose must return the same interface instance across invocations";
}
+// @VsrTest = VSR-5.5-002.001
TEST_P(AudioCoreSoundDose, GetSetOutputRs2UpperBound) {
if (soundDose == nullptr) {
GTEST_SKIP() << "SoundDose is not supported";
@@ -3557,6 +3597,7 @@
EXPECT_TRUE(isSupported) << "Getting/Setting RS2 upper bound must be supported";
}
+// @VsrTest = VSR-5.5-002.001
TEST_P(AudioCoreSoundDose, CheckDefaultRs2UpperBound) {
if (soundDose == nullptr) {
GTEST_SKIP() << "SoundDose is not supported";
@@ -3567,6 +3608,7 @@
EXPECT_EQ(rs2Value, ISoundDose::DEFAULT_MAX_RS2);
}
+// @VsrTest = VSR-5.5-002.001
TEST_P(AudioCoreSoundDose, RegisterSoundDoseCallbackTwiceThrowsException) {
if (soundDose == nullptr) {
GTEST_SKIP() << "SoundDose is not supported";
@@ -3577,6 +3619,7 @@
<< "Registering sound dose callback twice should throw EX_ILLEGAL_STATE";
}
+// @VsrTest = VSR-5.5-002.001
TEST_P(AudioCoreSoundDose, RegisterSoundDoseNullCallbackThrowsException) {
if (soundDose == nullptr) {
GTEST_SKIP() << "SoundDose is not supported";
diff --git a/audio/aidl/vts/VtsHalAudioCoreTargetTest.xml b/audio/aidl/vts/VtsHalAudioCoreTargetTest.xml
new file mode 100644
index 0000000..dfc1039
--- /dev/null
+++ b/audio/aidl/vts/VtsHalAudioCoreTargetTest.xml
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2023 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<configuration description="Runs VtsHalAudioCoreTargetTest.">
+ <option name="test-suite-tag" value="apct" />
+ <option name="test-suite-tag" value="apct-native" />
+
+ <target_preparer class="com.android.tradefed.targetprep.RootTargetPreparer"/>
+ <target_preparer class="com.android.tradefed.targetprep.StopServicesSetup"/>
+
+ <target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer">
+ <option name="run-command" value="setprop vts.native_server.on 1"/>
+ <option name="teardown-command" value="setprop vts.native_server.on 0"/>
+ </target_preparer>
+
+ <test class="com.android.tradefed.testtype.GTest" >
+ <option name="native-test-device-path" value="/data/local/tmp" />
+ <option name="module-name" value="VtsHalAudioCoreTargetTest" />
+ <option name="native-test-timeout" value="10m" />
+ </test>
+</configuration>
diff --git a/audio/aidl/vts/VtsHalAudioEffectTargetTest.cpp b/audio/aidl/vts/VtsHalAudioEffectTargetTest.cpp
index df66bd3..d8ad6c9 100644
--- a/audio/aidl/vts/VtsHalAudioEffectTargetTest.cpp
+++ b/audio/aidl/vts/VtsHalAudioEffectTargetTest.cpp
@@ -139,7 +139,10 @@
Descriptor desc;
ASSERT_NO_FATAL_FAILURE(create(mFactory, mEffect, mDescriptor));
ASSERT_NO_FATAL_FAILURE(getDescriptor(mEffect, desc));
- EXPECT_EQ(mDescriptor.common, desc.common);
+ EXPECT_EQ(mDescriptor.common.id.type, desc.common.id.type);
+ EXPECT_EQ(mDescriptor.common.id.uuid, desc.common.id.uuid);
+ EXPECT_EQ(mDescriptor.common.name, desc.common.name);
+ EXPECT_EQ(mDescriptor.common.implementor, desc.common.implementor);
// Effect implementation Must fill in implementor and name
EXPECT_NE("", desc.common.name);
EXPECT_NE("", desc.common.implementor);
@@ -176,7 +179,11 @@
ASSERT_NO_FATAL_FAILURE(create(mFactory, mEffect, mDescriptor));
ASSERT_NO_FATAL_FAILURE(getDescriptor(mEffect, desc));
- EXPECT_EQ(1ul, idSet.count(desc.common.id));
+ int uuidCount = std::count_if(idSet.begin(), idSet.end(), [&](const auto& id) {
+ return id.uuid == desc.common.id.uuid && id.type == desc.common.id.type;
+ });
+
+ EXPECT_EQ(1, uuidCount);
ASSERT_NO_FATAL_FAILURE(destroy(mFactory, mEffect));
}
diff --git a/audio/aidl/vts/VtsHalDynamicsProcessingTest.cpp b/audio/aidl/vts/VtsHalDynamicsProcessingTest.cpp
index c62a24e..033e3b5 100644
--- a/audio/aidl/vts/VtsHalDynamicsProcessingTest.cpp
+++ b/audio/aidl/vts/VtsHalDynamicsProcessingTest.cpp
@@ -25,8 +25,10 @@
#include <Utils.h>
#include "EffectHelper.h"
+#include "EffectRangeSpecific.h"
using namespace android;
+using namespace aidl::android::hardware::audio::effect::DynamicsProcessingRanges;
using aidl::android::hardware::audio::effect::Descriptor;
using aidl::android::hardware::audio::effect::DynamicsProcessing;
@@ -95,6 +97,19 @@
template <typename T>
bool isAidlVectorEqual(const std::vector<T>& source, const std::vector<T>& target);
+ template <typename T>
+ bool isChannelConfigValid(const std::vector<T>& cfgs) {
+ auto& channelCount = mChannelCount;
+ return std::all_of(cfgs.cbegin(), cfgs.cend(), [channelCount](const T& cfg) {
+ return (cfg.channel >= 0 && cfg.channel < channelCount);
+ });
+ }
+
+ template <typename T>
+ bool isBandConfigValid(const std::vector<T>& cfgs, int bandCount);
+
+ bool isParamValid(const DynamicsProcessing::Tag& tag, const DynamicsProcessing& dp);
+
// get set params and validate
void SetAndGetDynamicsProcessingParameters();
@@ -133,9 +148,11 @@
static const std::set<DynamicsProcessing::StageEnablement> kStageEnablementTestSet;
static const std::set<std::vector<DynamicsProcessing::InputGain>> kInputGainTestSet;
+ protected:
+ int mChannelCount;
+
private:
int32_t mChannelLayout;
- int mChannelCount;
std::vector<std::pair<DynamicsProcessing::Tag, DynamicsProcessing>> mTags;
void CleanUp() {
mTags.clear();
@@ -152,6 +169,8 @@
{.inUse = true, .bandCount = DynamicsProcessingTestHelper::kBandCount},
{.inUse = true, .bandCount = 0},
{.inUse = true, .bandCount = -1},
+ {.inUse = false, .bandCount = 0},
+ {.inUse = false, .bandCount = -1},
{.inUse = false, .bandCount = DynamicsProcessingTestHelper::kBandCount}};
// test value set for DynamicsProcessing::ChannelConfig
@@ -161,9 +180,7 @@
{.channel = 0, .enable = true},
{.channel = 1, .enable = false},
{.channel = 2, .enable = true}},
-
{{.channel = -1, .enable = false}, {.channel = 2, .enable = true}},
-
{{.channel = 0, .enable = true}, {.channel = 1, .enable = true}}};
// test value set for DynamicsProcessing::InputGain
@@ -172,10 +189,65 @@
{{.channel = 0, .gainDb = 10.f},
{.channel = 1, .gainDb = 0.f},
{.channel = 2, .gainDb = -10.f}},
-
{{.channel = -1, .gainDb = -10.f}, {.channel = -2, .gainDb = 10.f}},
+ {{.channel = -1, .gainDb = 10.f}, {.channel = 0, .gainDb = -10.f}},
+ {{.channel = 0, .gainDb = 10.f}, {.channel = 1, .gainDb = -10.f}}};
- {{.channel = -1, .gainDb = 10.f}, {.channel = 0, .gainDb = -10.f}}};
+template <typename T>
+bool DynamicsProcessingTestHelper::isBandConfigValid(const std::vector<T>& cfgs, int bandCount) {
+ std::vector<float> freqs(cfgs.size(), -1);
+ for (auto cfg : cfgs) {
+ if (cfg.channel < 0 || cfg.channel >= mChannelCount) return false;
+ if (cfg.band < 0 || cfg.band >= bandCount) return false;
+ freqs[cfg.band] = cfg.cutoffFrequencyHz;
+ }
+ if (std::count(freqs.begin(), freqs.end(), -1)) return false;
+ return std::is_sorted(freqs.begin(), freqs.end());
+}
+
+bool DynamicsProcessingTestHelper::isParamValid(const DynamicsProcessing::Tag& tag,
+ const DynamicsProcessing& dp) {
+ switch (tag) {
+ case DynamicsProcessing::preEq: {
+ if (!mEngineConfigApplied.preEqStage.inUse) return false;
+ return isChannelConfigValid(dp.get<DynamicsProcessing::preEq>());
+ }
+ case DynamicsProcessing::postEq: {
+ if (!mEngineConfigApplied.postEqStage.inUse) return false;
+ return isChannelConfigValid(dp.get<DynamicsProcessing::postEq>());
+ }
+ case DynamicsProcessing::mbc: {
+ if (!mEngineConfigApplied.mbcStage.inUse) return false;
+ return isChannelConfigValid(dp.get<DynamicsProcessing::mbc>());
+ }
+ case DynamicsProcessing::preEqBand: {
+ if (!mEngineConfigApplied.preEqStage.inUse) return false;
+ return isBandConfigValid(dp.get<DynamicsProcessing::preEqBand>(),
+ mEngineConfigApplied.preEqStage.bandCount);
+ }
+ case DynamicsProcessing::postEqBand: {
+ if (!mEngineConfigApplied.postEqStage.inUse) return false;
+ return isBandConfigValid(dp.get<DynamicsProcessing::postEqBand>(),
+ mEngineConfigApplied.postEqStage.bandCount);
+ }
+ case DynamicsProcessing::mbcBand: {
+ if (!mEngineConfigApplied.mbcStage.inUse) return false;
+ return isBandConfigValid(dp.get<DynamicsProcessing::mbcBand>(),
+ mEngineConfigApplied.mbcStage.bandCount);
+ }
+ case DynamicsProcessing::limiter: {
+ if (!mEngineConfigApplied.limiterInUse) return false;
+ return isChannelConfigValid(dp.get<DynamicsProcessing::limiter>());
+ }
+ case DynamicsProcessing::inputGain: {
+ return isChannelConfigValid(dp.get<DynamicsProcessing::inputGain>());
+ }
+ default: {
+ return true;
+ }
+ }
+ return true;
+}
bool DynamicsProcessingTestHelper::isParamEqual(const DynamicsProcessing::Tag& tag,
const DynamicsProcessing& dpRef,
@@ -270,8 +342,8 @@
// validate parameter
Descriptor desc;
ASSERT_STATUS(EX_NONE, mEffect->getDescriptor(&desc));
- const bool valid =
- isParameterValid<DynamicsProcessing, Range::dynamicsProcessing>(dp, desc);
+ bool valid = isParamInRange(dp, desc.capability.range.get<Range::dynamicsProcessing>());
+ if (valid) valid = isParamValid(tag, dp);
const binder_exception_t expected = valid ? EX_NONE : EX_ILLEGAL_ARGUMENT;
// set parameter
@@ -429,10 +501,11 @@
::testing::Combine(
testing::ValuesIn(EffectFactoryHelper::getAllEffectDescriptors(
IFactory::descriptor, getEffectTypeUuidDynamicsProcessing())),
- testing::Values(DynamicsProcessing::ResolutionPreference::FAVOR_TIME_RESOLUTION,
- DynamicsProcessing::ResolutionPreference::
- FAVOR_FREQUENCY_RESOLUTION), // variant
- testing::Values(-10.f, 0.f, 10.f), // processing duration
+ testing::Values(
+ DynamicsProcessing::ResolutionPreference::FAVOR_TIME_RESOLUTION,
+ DynamicsProcessing::ResolutionPreference::FAVOR_FREQUENCY_RESOLUTION,
+ static_cast<DynamicsProcessing::ResolutionPreference>(-1)), // variant
+ testing::Values(-10.f, 0.f, 10.f), // processing duration
testing::ValuesIn(
DynamicsProcessingTestHelper::kStageEnablementTestSet), // preEQ/postEQ/mbc
testing::Bool()), // limiter enable
@@ -515,12 +588,12 @@
LIMITER_MAX_NUM,
};
using LimiterConfigTestAdditional = std::array<float, LIMITER_MAX_NUM>;
-// attachTime, releaseTime, ratio, thresh, postGain
+// attackTime, releaseTime, ratio, thresh, postGain
static constexpr std::array<LimiterConfigTestAdditional, 4> kLimiterConfigTestAdditionalParam = {
{{-1, -60, -2.5, -2, -3.14},
{-1, 60, -2.5, 2, -3.14},
{1, -60, 2.5, -2, 3.14},
- {1, 60, 2.5, 2, 3.14}}};
+ {1, 60, 2.5, -2, 3.14}}};
using LimiterConfigTestParams =
std::tuple<std::pair<std::shared_ptr<IFactory>, Descriptor>, int32_t, bool, int32_t, bool,
@@ -669,15 +742,13 @@
enum EqBandConfigTestParamName {
EQ_BAND_INSTANCE_NAME,
EQ_BAND_CHANNEL,
- EQ_BAND_CHANNEL_ENABLE,
EQ_BAND_ENABLE,
EQ_BAND_CUT_OFF_FREQ,
EQ_BAND_GAIN,
EQ_BAND_STAGE_IN_USE
};
using EqBandConfigTestParams = std::tuple<std::pair<std::shared_ptr<IFactory>, Descriptor>, int32_t,
- std::vector<DynamicsProcessing::ChannelConfig>, bool,
- std::vector<std::pair<int, float>>, float, bool>;
+ bool, std::vector<std::pair<int, float>>, float, bool>;
void fillEqBandConfig(std::vector<DynamicsProcessing::EqBandConfig>& cfgs,
const EqBandConfigTestParams& params) {
@@ -698,8 +769,7 @@
public:
DynamicsProcessingTestEqBandConfig()
: DynamicsProcessingTestHelper(std::get<EQ_BAND_INSTANCE_NAME>(GetParam())),
- mStageInUse(std::get<EQ_BAND_STAGE_IN_USE>(GetParam())),
- mChannelConfig(std::get<EQ_BAND_CHANNEL_ENABLE>(GetParam())) {
+ mStageInUse(std::get<EQ_BAND_STAGE_IN_USE>(GetParam())) {
fillEqBandConfig(mCfgs, GetParam());
}
@@ -709,14 +779,18 @@
std::vector<DynamicsProcessing::EqBandConfig> mCfgs;
const bool mStageInUse;
- const std::vector<DynamicsProcessing::ChannelConfig> mChannelConfig;
};
TEST_P(DynamicsProcessingTestEqBandConfig, SetAndGetPreEqBandConfig) {
mEngineConfigPreset.preEqStage.inUse = mStageInUse;
mEngineConfigPreset.preEqStage.bandCount = mCfgs.size();
EXPECT_NO_FATAL_FAILURE(addEngineConfig(mEngineConfigPreset));
- EXPECT_NO_FATAL_FAILURE(addPreEqChannelConfig(mChannelConfig));
+ std::vector<DynamicsProcessing::ChannelConfig> cfgs(mChannelCount);
+ for (int i = 0; i < mChannelCount; i++) {
+ cfgs[i].channel = i;
+ cfgs[i].enable = true;
+ }
+ EXPECT_NO_FATAL_FAILURE(addPreEqChannelConfig(cfgs));
EXPECT_NO_FATAL_FAILURE(addPreEqBandConfigs(mCfgs));
SetAndGetDynamicsProcessingParameters();
}
@@ -725,7 +799,12 @@
mEngineConfigPreset.postEqStage.inUse = mStageInUse;
mEngineConfigPreset.postEqStage.bandCount = mCfgs.size();
EXPECT_NO_FATAL_FAILURE(addEngineConfig(mEngineConfigPreset));
- EXPECT_NO_FATAL_FAILURE(addPostEqChannelConfig(mChannelConfig));
+ std::vector<DynamicsProcessing::ChannelConfig> cfgs(mChannelCount);
+ for (int i = 0; i < mChannelCount; i++) {
+ cfgs[i].channel = i;
+ cfgs[i].enable = true;
+ }
+ EXPECT_NO_FATAL_FAILURE(addPostEqChannelConfig(cfgs));
EXPECT_NO_FATAL_FAILURE(addPostEqBandConfigs(mCfgs));
SetAndGetDynamicsProcessingParameters();
}
@@ -776,28 +855,23 @@
INSTANTIATE_TEST_SUITE_P(
DynamicsProcessingTest, DynamicsProcessingTestEqBandConfig,
- ::testing::Combine(
- testing::ValuesIn(EffectFactoryHelper::getAllEffectDescriptors(
- IFactory::descriptor, getEffectTypeUuidDynamicsProcessing())),
- testing::Values(-1, 0, 10), // channel ID
- testing::ValuesIn(
- DynamicsProcessingTestHelper::kChannelConfigTestSet), // channel enable
- testing::Bool(), // band enable
- testing::ValuesIn(kBands), // cut off frequencies
- testing::Values(-3.14f, 3.14f), // gain
- testing::Bool()), // stage in use
+ ::testing::Combine(testing::ValuesIn(EffectFactoryHelper::getAllEffectDescriptors(
+ IFactory::descriptor, getEffectTypeUuidDynamicsProcessing())),
+ testing::Values(-1, 0, 10), // channel ID
+ testing::Bool(), // band enable
+ testing::ValuesIn(kBands), // cut off frequencies
+ testing::Values(-3.14f, 3.14f), // gain
+ testing::Values(true)), // stage in use
[](const auto& info) {
auto descriptor = std::get<EQ_BAND_INSTANCE_NAME>(info.param).second;
std::vector<DynamicsProcessing::EqBandConfig> cfgs;
fillEqBandConfig(cfgs, info.param);
- std::string enable =
- ::android::internal::ToString(std::get<EQ_BAND_CHANNEL_ENABLE>(info.param));
std::string bands = ::android::internal::ToString(cfgs);
std::string stageInUse = std::to_string(std::get<EQ_BAND_STAGE_IN_USE>(info.param));
std::string name = "Implementor_" + descriptor.common.implementor + "_name_" +
descriptor.common.name + "_UUID_" +
- descriptor.common.id.uuid.toString() + "_" + enable + "_bands_" +
- bands + "_stageInUse_" + stageInUse;
+ descriptor.common.id.uuid.toString() + "_bands_" + bands +
+ "_stageInUse_" + stageInUse;
std::replace_if(
name.begin(), name.end(), [](const char c) { return !std::isalnum(c); }, '_');
return name;
@@ -811,7 +885,6 @@
enum MbcBandConfigParamName {
MBC_BAND_INSTANCE_NAME,
MBC_BAND_CHANNEL,
- MBC_BAND_CHANNEL_CONFIG,
MBC_BAND_ENABLE,
MBC_BAND_CUTOFF_FREQ,
MBC_BAND_STAGE_IN_USE,
@@ -831,16 +904,15 @@
};
using TestParamsMbcBandConfigAdditional = std::array<float, MBC_ADD_MAX_NUM>;
-// attachTime, releaseTime, ratio, thresh, kneeWidth, noise, expander, preGain, postGain
+// attackTime, releaseTime, ratio, thresh, kneeWidth, noise, expander, preGain, postGain
static constexpr std::array<TestParamsMbcBandConfigAdditional, 4> kMbcBandConfigAdditionalParam = {
{{-3, -10, -2, -2, -5, -90, -2.5, -2, -2},
{0, 0, 0, 0, 0, 0, 0, 0, 0},
{-3, 10, -2, 2, -5, 90, -2.5, 2, -2},
- {3, 10, 2, 2, 5, 90, 2.5, 2, 2}}};
+ {3, 10, 2, -2, -5, 90, 2.5, 2, 2}}};
using TestParamsMbcBandConfig =
- std::tuple<std::pair<std::shared_ptr<IFactory>, Descriptor>, int32_t,
- std::vector<DynamicsProcessing::ChannelConfig>, bool,
+ std::tuple<std::pair<std::shared_ptr<IFactory>, Descriptor>, int32_t, bool,
std::vector<std::pair<int, float>>, bool, TestParamsMbcBandConfigAdditional>;
void fillMbcBandConfig(std::vector<DynamicsProcessing::MbcBandConfig>& cfgs,
@@ -873,8 +945,7 @@
public:
DynamicsProcessingTestMbcBandConfig()
: DynamicsProcessingTestHelper(std::get<MBC_BAND_INSTANCE_NAME>(GetParam())),
- mStageInUse(std::get<MBC_BAND_STAGE_IN_USE>(GetParam())),
- mChannelConfig(std::get<MBC_BAND_CHANNEL_CONFIG>(GetParam())) {
+ mStageInUse(std::get<MBC_BAND_STAGE_IN_USE>(GetParam())) {
fillMbcBandConfig(mCfgs, GetParam());
}
@@ -884,42 +955,41 @@
std::vector<DynamicsProcessing::MbcBandConfig> mCfgs;
const bool mStageInUse;
- const std::vector<DynamicsProcessing::ChannelConfig> mChannelConfig;
};
TEST_P(DynamicsProcessingTestMbcBandConfig, SetAndGetMbcBandConfig) {
mEngineConfigPreset.mbcStage.inUse = mStageInUse;
mEngineConfigPreset.mbcStage.bandCount = mCfgs.size();
EXPECT_NO_FATAL_FAILURE(addEngineConfig(mEngineConfigPreset));
- EXPECT_NO_FATAL_FAILURE(addMbcChannelConfig(mChannelConfig));
+ std::vector<DynamicsProcessing::ChannelConfig> cfgs(mChannelCount);
+ for (int i = 0; i < mChannelCount; i++) {
+ cfgs[i].channel = i;
+ cfgs[i].enable = true;
+ }
+ EXPECT_NO_FATAL_FAILURE(addMbcChannelConfig(cfgs));
EXPECT_NO_FATAL_FAILURE(addMbcBandConfigs(mCfgs));
SetAndGetDynamicsProcessingParameters();
}
INSTANTIATE_TEST_SUITE_P(
DynamicsProcessingTest, DynamicsProcessingTestMbcBandConfig,
- ::testing::Combine(
- testing::ValuesIn(EffectFactoryHelper::getAllEffectDescriptors(
- IFactory::descriptor, getEffectTypeUuidDynamicsProcessing())),
- testing::Values(-1, 0, 10), // channel count
- testing::ValuesIn(
- DynamicsProcessingTestHelper::kChannelConfigTestSet), // channel config
- testing::Bool(), // enable
- testing::ValuesIn(kBands), // cut off frequencies
- testing::Bool(), // stage in use
- testing::ValuesIn(kMbcBandConfigAdditionalParam)), // Additional
+ ::testing::Combine(testing::ValuesIn(EffectFactoryHelper::getAllEffectDescriptors(
+ IFactory::descriptor, getEffectTypeUuidDynamicsProcessing())),
+ testing::Values(-1, 0, 10), // channel count
+ testing::Bool(), // enable
+ testing::ValuesIn(kBands), // cut off frequencies
+ testing::Bool(), // stage in use
+ testing::ValuesIn(kMbcBandConfigAdditionalParam)), // Additional
[](const auto& info) {
auto descriptor = std::get<MBC_BAND_INSTANCE_NAME>(info.param).second;
std::vector<DynamicsProcessing::MbcBandConfig> cfgs;
fillMbcBandConfig(cfgs, info.param);
- std::string enable =
- ::android::internal::ToString(std::get<MBC_BAND_CHANNEL_CONFIG>(info.param));
std::string mbcBands = ::android::internal::ToString(cfgs);
std::string stageInUse = std::to_string(std::get<MBC_BAND_STAGE_IN_USE>(info.param));
std::string name = "Implementor_" + descriptor.common.implementor + "_name_" +
descriptor.common.name + "_UUID_" +
- descriptor.common.id.uuid.toString() + "_enable_" + enable +
- "_bands_" + mbcBands + "_stageInUse_" + stageInUse;
+ descriptor.common.id.uuid.toString() + "_bands_" + mbcBands +
+ "_stageInUse_" + stageInUse;
std::replace_if(
name.begin(), name.end(), [](const char c) { return !std::isalnum(c); }, '_');
return name;
diff --git a/automotive/audiocontrol/1.0/default/test/fuzzer/Android.bp b/automotive/audiocontrol/1.0/default/test/fuzzer/Android.bp
index 78f5b52..4308d52 100644
--- a/automotive/audiocontrol/1.0/default/test/fuzzer/Android.bp
+++ b/automotive/audiocontrol/1.0/default/test/fuzzer/Android.bp
@@ -48,5 +48,13 @@
"android-media-fuzzing-reports@google.com",
],
componentid: 533764,
+ hotlists: [
+ "4593311",
+ ],
+ description: "The fuzzer targets the APIs of android.hardware.automotive.audiocontrol@1.0-service binary",
+ vector: "local_privileges_required",
+ service_privilege: "privileged",
+ users: "multi_user",
+ fuzzed_code_usage: "shipped",
},
}
diff --git a/automotive/audiocontrol/aidl/default/audiocontrol-default.xml b/automotive/audiocontrol/aidl/default/audiocontrol-default.xml
index 3536bb9..95cd7f0 100644
--- a/automotive/audiocontrol/aidl/default/audiocontrol-default.xml
+++ b/automotive/audiocontrol/aidl/default/audiocontrol-default.xml
@@ -1,7 +1,7 @@
<manifest version="2.0" type="device">
<hal format="aidl">
<name>android.hardware.automotive.audiocontrol</name>
- <version>2</version>
+ <version>3</version>
<fqname>IAudioControl/default</fqname>
</hal>
</manifest>
diff --git a/automotive/can/1.0/default/tests/fuzzer/Android.bp b/automotive/can/1.0/default/tests/fuzzer/Android.bp
index 52b43b0..de0b96f 100644
--- a/automotive/can/1.0/default/tests/fuzzer/Android.bp
+++ b/automotive/can/1.0/default/tests/fuzzer/Android.bp
@@ -50,5 +50,13 @@
"android-media-fuzzing-reports@google.com",
],
componentid: 533764,
+ hotlists: [
+ "4593311",
+ ],
+ description: "The fuzzer targets the APIs of android.hardware.automotive.can@1.0-service",
+ vector: "local_no_privileges_required",
+ service_privilege: "privileged",
+ users: "multi_user",
+ fuzzed_code_usage: "shipped",
},
}
diff --git a/automotive/evs/aidl/impl/Android.bp b/automotive/evs/aidl/impl/Android.bp
index 0b51a0c..fa44ebb 100644
--- a/automotive/evs/aidl/impl/Android.bp
+++ b/automotive/evs/aidl/impl/Android.bp
@@ -22,7 +22,7 @@
name: "EvsHalDefaults",
defaults: ["android.hardware.graphics.common-ndk_static"],
static_libs: [
- "android.hardware.automotive.evs-V1-ndk",
+ "android.hardware.automotive.evs-V2-ndk",
"android.hardware.common-V2-ndk",
],
shared_libs: [
diff --git a/automotive/evs/aidl/impl/default/Android.bp b/automotive/evs/aidl/impl/default/Android.bp
index dbe0314..70c523b 100644
--- a/automotive/evs/aidl/impl/default/Android.bp
+++ b/automotive/evs/aidl/impl/default/Android.bp
@@ -24,13 +24,56 @@
cc_binary {
name: "android.hardware.automotive.evs-aidl-default-service",
defaults: ["EvsHalDefaults"],
- local_include_dirs: ["include"],
- vintf_fragments: ["evs-default-service.xml"],
+ vintf_fragments: ["manifest_evs-default-service.xml"],
init_rc: ["evs-default-service.rc"],
vendor: true,
relative_install_path: "hw",
- srcs: ["src/*.cpp"],
- shared_libs: [
- "libbinder_ndk",
+ cflags: [
+ "-DGL_GLEXT_PROTOTYPES",
+ "-DEGL_EGLEXT_PROTOTYPES",
+ "-Wall",
+ "-Wextra",
+ "-Werror",
+ "-Wthread-safety",
],
+ srcs: [
+ ":libgui_frame_event_aidl",
+ "src/*.cpp",
+ ],
+ shared_libs: [
+ "android.hardware.graphics.bufferqueue@1.0",
+ "android.hardware.graphics.bufferqueue@2.0",
+ "android.hidl.token@1.0-utils",
+ "libEGL",
+ "libGLESv2",
+ "libbase",
+ "libbinder_ndk",
+ "libbufferqueueconverter",
+ "libcamera_metadata",
+ "libhardware_legacy",
+ "libhidlbase",
+ "liblog",
+ "libnativewindow",
+ "libtinyxml2",
+ "libui",
+ "libutils",
+ "libyuv",
+ ],
+ static_libs: [
+ "android.frameworks.automotive.display-V1-ndk",
+ "android.hardware.automotive.evs-V2-ndk",
+ "android.hardware.common-V2-ndk",
+ "libaidlcommonsupport",
+ "libcutils",
+ ],
+ local_include_dirs: ["include"],
+ include_dirs: ["frameworks/native/include/"],
+ required: ["evs_mock_hal_configuration.xml"],
+}
+
+prebuilt_etc {
+ name: "evs_mock_hal_configuration.xml",
+ soc_specific: true,
+ src: "resources/evs_mock_configuration.xml",
+ sub_dir: "automotive/evs",
}
diff --git a/automotive/evs/aidl/impl/default/evs-default-service.rc b/automotive/evs/aidl/impl/default/evs-default-service.rc
index ea8e689..3da41ff 100644
--- a/automotive/evs/aidl/impl/default/evs-default-service.rc
+++ b/automotive/evs/aidl/impl/default/evs-default-service.rc
@@ -1,5 +1,8 @@
service vendor.evs-hal-default /vendor/bin/hw/android.hardware.automotive.evs-aidl-default-service
class early_hal
- user automotive_evs
- group automotive_evs
+ priority -20
+ user graphics
+ group automotive_evs camera
+ onrestart restart cardisplayproxyd
+ onrestart restart evsmanagerd
disabled
diff --git a/automotive/evs/aidl/impl/default/evs-default-service.xml b/automotive/evs/aidl/impl/default/evs-default-service.xml
deleted file mode 100644
index 96ff9f6..0000000
--- a/automotive/evs/aidl/impl/default/evs-default-service.xml
+++ /dev/null
@@ -1,11 +0,0 @@
-<manifest version="1.0" type="device">
- <hal format="aidl">
- <name>android.hardware.automotive.evs</name>
- <transport>hwbinder</transport>
- <version>1</version>
- <interface>
- <name>IEvsEnumerator</name>
- <instance>hw/0</instance>
- </interface>
- </hal>
-</manifest>
diff --git a/automotive/evs/aidl/impl/default/include/ConfigManager.h b/automotive/evs/aidl/impl/default/include/ConfigManager.h
new file mode 100644
index 0000000..1d5fe77
--- /dev/null
+++ b/automotive/evs/aidl/impl/default/include/ConfigManager.h
@@ -0,0 +1,384 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include "ConfigManagerUtil.h"
+
+#include <aidl/android/hardware/automotive/evs/CameraParam.h>
+#include <aidl/android/hardware/graphics/common/PixelFormat.h>
+#include <android-base/logging.h>
+#include <system/camera_metadata.h>
+
+#include <tinyxml2.h>
+
+#include <string>
+#include <string_view>
+#include <unordered_map>
+#include <unordered_set>
+#include <vector>
+
+/*
+ * Please note that this is different from what is defined in
+ * libhardware/modules/camera/3_4/metadata/types.h; this has one additional
+ * field to store a framerate.
+ */
+typedef struct {
+ int id;
+ int width;
+ int height;
+ ::aidl::android::hardware::graphics::common::PixelFormat format;
+ int type;
+ int framerate;
+} StreamConfiguration;
+
+class ConfigManager final {
+ public:
+ static std::unique_ptr<ConfigManager> Create();
+ ConfigManager(const ConfigManager&) = delete;
+ ConfigManager& operator=(const ConfigManager&) = delete;
+
+ /* Camera device's capabilities and metadata */
+ class CameraInfo {
+ public:
+ CameraInfo() : characteristics(nullptr) {}
+
+ virtual ~CameraInfo();
+
+ /* Allocate memory for camera_metadata_t */
+ bool allocate(size_t entry_cap, size_t data_cap) {
+ if (characteristics != nullptr) {
+ LOG(ERROR) << "Camera metadata is already allocated";
+ return false;
+ }
+
+ characteristics = allocate_camera_metadata(entry_cap, data_cap);
+ return characteristics != nullptr;
+ }
+
+ /*
+ * List of supported controls that the primary client can program.
+ * Paraemters are stored with its valid range
+ */
+ std::unordered_map<::aidl::android::hardware::automotive::evs::CameraParam,
+ std::tuple<int32_t, int32_t, int32_t>>
+ controls;
+
+ /*
+ * List of supported output stream configurations.
+ */
+ std::unordered_map<int32_t, StreamConfiguration> streamConfigurations;
+
+ /*
+ * Internal storage for camera metadata. Each entry holds a pointer to
+ * data and number of elements
+ */
+ std::unordered_map<camera_metadata_tag_t, std::pair<void*, size_t>> cameraMetadata;
+
+ /* Camera module characteristics */
+ camera_metadata_t* characteristics;
+ };
+
+ class CameraGroupInfo : public CameraInfo {
+ public:
+ CameraGroupInfo() {}
+
+ /* ID of member camera devices */
+ std::unordered_set<std::string> devices;
+
+ /* The capture operation of member camera devices are synchronized */
+ int32_t synchronized = 0;
+ };
+
+ class SystemInfo {
+ public:
+ /* number of available cameras */
+ int32_t numCameras = 0;
+ };
+
+ class DisplayInfo {
+ public:
+ /*
+ * List of supported input stream configurations.
+ */
+ std::unordered_map<int32_t, StreamConfiguration> streamConfigurations;
+ };
+
+ /*
+ * Return system information
+ *
+ * @return SystemInfo
+ * Constant reference of SystemInfo.
+ */
+ const SystemInfo& getSystemInfo() {
+ std::unique_lock<std::mutex> lock(mConfigLock);
+ mConfigCond.wait(lock, [this] { return mIsReady; });
+ return mSystemInfo;
+ }
+
+ /*
+ * Return a list of camera identifiers
+ *
+ * This function assumes that it is not being called frequently.
+ *
+ * @return std::vector<std::string>
+ * A vector that contains unique camera device identifiers.
+ */
+ std::vector<std::string> getCameraIdList() {
+ std::unique_lock<std::mutex> lock(mConfigLock);
+ mConfigCond.wait(lock, [this] { return mIsReady; });
+
+ std::vector<std::string> aList;
+ aList.reserve(mCameraInfo.size());
+ for (auto&& v : mCameraInfo) {
+ aList.push_back(v.first);
+ }
+
+ return aList;
+ }
+
+ /*
+ * Return a list of camera group identifiers
+ *
+ * This function assumes that it is not being called frequently.
+ *
+ * @return std::vector<std::string>
+ * A vector that contains unique camera device identifiers.
+ */
+ std::vector<std::string> getCameraGroupIdList() {
+ std::unique_lock<std::mutex> lock(mConfigLock);
+ mConfigCond.wait(lock, [this] { return mIsReady; });
+
+ std::vector<std::string> aList;
+ aList.reserve(mCameraGroups.size());
+ for (auto&& v : mCameraGroups) {
+ aList.push_back(v.first);
+ }
+
+ return aList;
+ }
+
+ /*
+ * Return a pointer to the camera group
+ *
+ * @return CameraGroup
+ * A pointer to a camera group identified by a given id.
+ */
+ std::unique_ptr<CameraGroupInfo>& getCameraGroupInfo(const std::string& gid) {
+ std::unique_lock<std::mutex> lock(mConfigLock);
+ mConfigCond.wait(lock, [this] { return mIsReady; });
+
+ return mCameraGroups[gid];
+ }
+
+ /*
+ * Return a camera metadata
+ *
+ * @param cameraId
+ * Unique camera node identifier in string
+ *
+ * @return unique_ptr<CameraInfo>
+ * A pointer to CameraInfo that is associated with a given camera
+ * ID. This returns a null pointer if this does not recognize a
+ * given camera identifier.
+ */
+ std::unique_ptr<CameraInfo>& getCameraInfo(const std::string& cameraId) noexcept {
+ std::unique_lock<std::mutex> lock(mConfigLock);
+ mConfigCond.wait(lock, [this] { return mIsReady; });
+
+ return mCameraInfo[cameraId];
+ }
+
+ /*
+ * Tell whether the configuration data is ready to be used
+ *
+ * @return bool
+ * True if configuration data is ready to be consumed.
+ */
+ bool isReady() const { return mIsReady; }
+
+ private:
+ /* Constructors */
+ ConfigManager() : mBinaryFilePath("") {}
+
+ static std::string_view sConfigDefaultPath;
+ static std::string_view sConfigOverridePath;
+
+ /* System configuration */
+ SystemInfo mSystemInfo;
+
+ /* Internal data structure for camera device information */
+ std::unordered_map<std::string, std::unique_ptr<CameraInfo>> mCameraInfo;
+
+ /* Internal data structure for camera device information */
+ std::unordered_map<std::string, std::unique_ptr<DisplayInfo>> mDisplayInfo;
+
+ /* Camera groups are stored in <groud id, CameraGroup> hash map */
+ std::unordered_map<std::string, std::unique_ptr<CameraGroupInfo>> mCameraGroups;
+
+ /*
+ * Camera positions are stored in <position, camera id set> hash map.
+ * The position must be one of front, rear, left, and right.
+ */
+ std::unordered_map<std::string, std::unordered_set<std::string>> mCameraPosition;
+
+ /* Configuration data lock */
+ mutable std::mutex mConfigLock;
+
+ /*
+ * This condition is signalled when it completes a configuration data
+ * preparation.
+ */
+ std::condition_variable mConfigCond;
+
+ /* A path to a binary configuration file */
+ const char* mBinaryFilePath;
+
+ /* Configuration data readiness */
+ bool mIsReady = false;
+
+ /*
+ * Parse a given EVS configuration file and store the information
+ * internally.
+ *
+ * @return bool
+ * True if it completes parsing a file successfully.
+ */
+ bool readConfigDataFromXML() noexcept;
+
+ /*
+ * read the information of the vehicle
+ *
+ * @param aSysElem
+ * A pointer to "system" XML element.
+ */
+ void readSystemInfo(const tinyxml2::XMLElement* const aSysElem);
+
+ /*
+ * read the information of camera devices
+ *
+ * @param aCameraElem
+ * A pointer to "camera" XML element that may contain multiple
+ * "device" elements.
+ */
+ void readCameraInfo(const tinyxml2::XMLElement* const aCameraElem);
+
+ /*
+ * read display device information
+ *
+ * @param aDisplayElem
+ * A pointer to "display" XML element that may contain multiple
+ * "device" elements.
+ */
+ void readDisplayInfo(const tinyxml2::XMLElement* const aDisplayElem);
+
+ /*
+ * read camera device information
+ *
+ * @param aCamera
+ * A pointer to CameraInfo that will be completed by this
+ * method.
+ * aDeviceElem
+ * A pointer to "device" XML element that contains camera module
+ * capability info and its characteristics.
+ *
+ * @return bool
+ * Return false upon any failure in reading and processing camera
+ * device information.
+ */
+ bool readCameraDeviceInfo(CameraInfo* aCamera, const tinyxml2::XMLElement* aDeviceElem);
+
+ /*
+ * read camera metadata
+ *
+ * @param aCapElem
+ * A pointer to "cap" XML element.
+ * @param aCamera
+ * A pointer to CameraInfo that is being filled by this method.
+ * @param dataSize
+ * Required size of memory to store camera metadata found in this
+ * method. This is calculated in this method and returned to the
+ * caller for camera_metadata allocation.
+ *
+ * @return size_t
+ * Number of camera metadata entries
+ */
+ size_t readCameraCapabilities(const tinyxml2::XMLElement* const aCapElem, CameraInfo* aCamera,
+ size_t& dataSize);
+
+ /*
+ * read camera metadata
+ *
+ * @param aParamElem
+ * A pointer to "characteristics" XML element.
+ * @param aCamera
+ * A pointer to CameraInfo that is being filled by this method.
+ * @param dataSize
+ * Required size of memory to store camera metadata found in this
+ * method.
+ *
+ * @return size_t
+ * Number of camera metadata entries
+ */
+ size_t readCameraMetadata(const tinyxml2::XMLElement* const aParamElem, CameraInfo* aCamera,
+ size_t& dataSize);
+
+ /*
+ * construct camera_metadata_t from camera capabilities and metadata
+ *
+ * @param aCamera
+ * A pointer to CameraInfo that is being filled by this method.
+ * @param totalEntries
+ * Number of camera metadata entries to be added.
+ * @param totalDataSize
+ * Sum of sizes of camera metadata entries to be added.
+ *
+ * @return bool
+ * False if either it fails to allocate memory for camera metadata
+ * or its size is not large enough to add all found camera metadata
+ * entries.
+ */
+ bool constructCameraMetadata(CameraInfo* aCamera, const size_t totalEntries,
+ const size_t totalDataSize);
+
+ /*
+ * Read configuration data from the binary file
+ *
+ * @return bool
+ * True if it succeeds to read configuration data from a binary
+ * file.
+ */
+ bool readConfigDataFromBinary();
+
+ /*
+ * Store configuration data to the file
+ *
+ * @return bool
+ * True if it succeeds to serialize mCameraInfo to the file.
+ */
+ bool writeConfigDataToBinary();
+
+ /*
+ * debugging method to print out all XML elements and their attributes in
+ * logcat message.
+ *
+ * @param aNode
+ * A pointer to the root XML element to navigate.
+ * @param prefix
+ * A prefix to XML string.
+ */
+ void printElementNames(const tinyxml2::XMLElement* aNode, const std::string& prefix = "") const;
+};
diff --git a/automotive/evs/aidl/impl/default/include/ConfigManagerUtil.h b/automotive/evs/aidl/impl/default/include/ConfigManagerUtil.h
new file mode 100644
index 0000000..32b50d3
--- /dev/null
+++ b/automotive/evs/aidl/impl/default/include/ConfigManagerUtil.h
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <aidl/android/hardware/automotive/evs/CameraParam.h>
+#include <aidl/android/hardware/graphics/common/PixelFormat.h>
+#include <android-base/macros.h>
+#include <system/camera_metadata.h>
+
+#include <string>
+#include <utility>
+
+class ConfigManagerUtil final {
+ public:
+ /**
+ * Convert a given string into V4L2_CID_*
+ */
+ static bool convertToEvsCameraParam(
+ const std::string& id,
+ ::aidl::android::hardware::automotive::evs::CameraParam& camParam);
+ /**
+ * Convert a given string into android.hardware.graphics.common.PixelFormat
+ */
+ static bool convertToPixelFormat(const std::string& in,
+ ::aidl::android::hardware::graphics::common::PixelFormat& out);
+ /**
+ * Convert a given string into corresponding camera metadata data tag defined in
+ * system/media/camera/include/system/camera_metadata_tags.h
+ */
+ static bool convertToMetadataTag(const char* name, camera_metadata_tag& aTag);
+ /**
+ * Convert a given string into a floating value array
+ */
+ static float* convertFloatArray(const char* sz, const char* vals, size_t& count,
+ const char delimiter = ',');
+ /**
+ * Trim a string
+ */
+ static std::string trimString(const std::string& src, const std::string& ws = " \n\r\t\f\v");
+
+ /**
+ * Convert a given string to corresponding camera capabilities
+ */
+ static bool convertToCameraCapability(
+ const char* name, camera_metadata_enum_android_request_available_capabilities_t& cap);
+
+ DISALLOW_IMPLICIT_CONSTRUCTORS(ConfigManagerUtil);
+};
diff --git a/automotive/evs/aidl/impl/default/include/DefaultEvsEnumerator.h b/automotive/evs/aidl/impl/default/include/DefaultEvsEnumerator.h
deleted file mode 100644
index 03a578d..0000000
--- a/automotive/evs/aidl/impl/default/include/DefaultEvsEnumerator.h
+++ /dev/null
@@ -1,66 +0,0 @@
-/*
- * Copyright (C) 2022 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef android_hardware_automotive_evs_aidl_impl_evshal_include_DefaultEvsHal_H_
-#define android_hardware_automotive_evs_aidl_impl_evshal_include_DefaultEvsHal_H_
-
-#include <aidl/android/hardware/automotive/evs/BnEvsEnumerator.h>
-
-namespace aidl::android::hardware::automotive::evs::implementation {
-
-class DefaultEvsEnumerator final
- : public ::aidl::android::hardware::automotive::evs::BnEvsEnumerator {
- ::ndk::ScopedAStatus isHardware(bool* flag) override;
- ::ndk::ScopedAStatus openCamera(
- const std::string& cameraId,
- const ::aidl::android::hardware::automotive::evs::Stream& streamConfig,
- std::shared_ptr<::aidl::android::hardware::automotive::evs::IEvsCamera>* obj) override;
- ::ndk::ScopedAStatus closeCamera(
- const std::shared_ptr<::aidl::android::hardware::automotive::evs::IEvsCamera>& obj)
- override;
- ::ndk::ScopedAStatus getCameraList(
- std::vector<::aidl::android::hardware::automotive::evs::CameraDesc>* list) override;
- ::ndk::ScopedAStatus getStreamList(
- const ::aidl::android::hardware::automotive::evs::CameraDesc& desc,
- std::vector<::aidl::android::hardware::automotive::evs::Stream>* _aidl_return) override;
- ::ndk::ScopedAStatus openDisplay(
- int32_t displayId,
- std::shared_ptr<::aidl::android::hardware::automotive::evs::IEvsDisplay>* obj) override;
- ::ndk::ScopedAStatus closeDisplay(
- const std::shared_ptr<::aidl::android::hardware::automotive::evs::IEvsDisplay>& obj)
- override;
- ::ndk::ScopedAStatus getDisplayIdList(std::vector<uint8_t>* list) override;
- ::ndk::ScopedAStatus getDisplayState(
- ::aidl::android::hardware::automotive::evs::DisplayState* state) override;
- ::ndk::ScopedAStatus registerStatusCallback(
- const std::shared_ptr<
- ::aidl::android::hardware::automotive::evs::IEvsEnumeratorStatusCallback>&
- callback) override;
- ::ndk::ScopedAStatus openUltrasonicsArray(
- const std::string& id,
- std::shared_ptr<::aidl::android::hardware::automotive::evs::IEvsUltrasonicsArray>* obj)
- override;
- ::ndk::ScopedAStatus closeUltrasonicsArray(
- const std::shared_ptr<::aidl::android::hardware::automotive::evs::IEvsUltrasonicsArray>&
- arr) override;
- ::ndk::ScopedAStatus getUltrasonicsArrayList(
- std::vector<::aidl::android::hardware::automotive::evs::UltrasonicsArrayDesc>* list)
- override;
-};
-
-} // namespace aidl::android::hardware::automotive::evs::implementation
-
-#endif // android_hardware_automotive_evs_aidl_impl_evshal_include_DefaultEvsHal_H_
diff --git a/automotive/evs/aidl/impl/default/include/EvsEnumerator.h b/automotive/evs/aidl/impl/default/include/EvsEnumerator.h
new file mode 100644
index 0000000..259c266
--- /dev/null
+++ b/automotive/evs/aidl/impl/default/include/EvsEnumerator.h
@@ -0,0 +1,137 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include "ConfigManager.h"
+#include "EvsGlDisplay.h"
+#include "EvsMockCamera.h"
+
+#include <aidl/android/frameworks/automotive/display/ICarDisplayProxy.h>
+#include <aidl/android/hardware/automotive/evs/BnEvsEnumerator.h>
+#include <aidl/android/hardware/automotive/evs/CameraDesc.h>
+#include <aidl/android/hardware/automotive/evs/DeviceStatusType.h>
+#include <aidl/android/hardware/automotive/evs/IEvsCamera.h>
+#include <aidl/android/hardware/automotive/evs/IEvsEnumeratorStatusCallback.h>
+#include <aidl/android/hardware/automotive/evs/Stream.h>
+#include <utils/Thread.h>
+
+#include <atomic>
+#include <mutex>
+#include <optional>
+#include <thread>
+#include <unordered_map>
+
+namespace aidl::android::hardware::automotive::evs::implementation {
+
+class EvsEnumerator final : public ::aidl::android::hardware::automotive::evs::BnEvsEnumerator {
+ public:
+ // Methods from ::aidl::android::hardware::automotive::evs::IEvsEnumerator
+ ndk::ScopedAStatus isHardware(bool* flag) override;
+ ndk::ScopedAStatus openCamera(const std::string& cameraId, const evs::Stream& streamConfig,
+ std::shared_ptr<evs::IEvsCamera>* obj) override;
+ ndk::ScopedAStatus closeCamera(const std::shared_ptr<evs::IEvsCamera>& obj) override;
+ ndk::ScopedAStatus getCameraList(std::vector<evs::CameraDesc>* _aidl_return) override;
+ ndk::ScopedAStatus getStreamList(const evs::CameraDesc& desc,
+ std::vector<evs::Stream>* _aidl_return) override;
+ ndk::ScopedAStatus openDisplay(int32_t displayId,
+ std::shared_ptr<evs::IEvsDisplay>* obj) override;
+ ndk::ScopedAStatus closeDisplay(const std::shared_ptr<evs::IEvsDisplay>& obj) override;
+ ndk::ScopedAStatus getDisplayIdList(std::vector<uint8_t>* list) override;
+ ndk::ScopedAStatus getDisplayState(evs::DisplayState* state) override;
+ ndk::ScopedAStatus getDisplayStateById(int32_t displayId, evs::DisplayState* state) override;
+ ndk::ScopedAStatus registerStatusCallback(
+ const std::shared_ptr<evs::IEvsEnumeratorStatusCallback>& callback) override;
+ ndk::ScopedAStatus openUltrasonicsArray(
+ const std::string& id, std::shared_ptr<evs::IEvsUltrasonicsArray>* obj) override;
+ ndk::ScopedAStatus closeUltrasonicsArray(
+ const std::shared_ptr<evs::IEvsUltrasonicsArray>& obj) override;
+ ndk::ScopedAStatus getUltrasonicsArrayList(
+ std::vector<evs::UltrasonicsArrayDesc>* list) override;
+
+ // Implementation details
+ EvsEnumerator(const std::shared_ptr<
+ ::aidl::android::frameworks::automotive::display::ICarDisplayProxy>&
+ proxyService);
+
+ void notifyDeviceStatusChange(const std::string_view& deviceName, evs::DeviceStatusType type);
+
+ private:
+ struct CameraRecord {
+ evs::CameraDesc desc;
+ std::weak_ptr<EvsMockCamera> activeInstance;
+
+ CameraRecord(const char* cameraId) : desc() { desc.id = cameraId; }
+ };
+
+ class ActiveDisplays {
+ public:
+ struct DisplayInfo {
+ int32_t id{-1};
+ std::weak_ptr<EvsGlDisplay> displayWeak;
+ uintptr_t internalDisplayRawAddr;
+ };
+
+ std::optional<DisplayInfo> popDisplay(int32_t id);
+
+ std::optional<DisplayInfo> popDisplay(const std::shared_ptr<IEvsDisplay>& display);
+
+ std::unordered_map<int32_t, DisplayInfo> getAllDisplays();
+
+ bool tryInsert(int32_t id, const std::shared_ptr<EvsGlDisplay>& display);
+
+ private:
+ std::mutex mMutex;
+ std::unordered_map<int32_t, DisplayInfo> mIdToDisplay GUARDED_BY(mMutex);
+ std::unordered_map<uintptr_t, int32_t> mDisplayToId GUARDED_BY(mMutex);
+ };
+
+ bool checkPermission();
+ void closeCamera_impl(const std::shared_ptr<evs::IEvsCamera>& pCamera,
+ const std::string& cameraId);
+ ndk::ScopedAStatus getDisplayStateImpl(std::optional<int32_t> displayId,
+ evs::DisplayState* state);
+
+ static bool qualifyCaptureDevice(const char* deviceName);
+ static CameraRecord* findCameraById(const std::string& cameraId);
+ static void enumerateCameras();
+ static bool addCaptureDevice(const std::string& deviceName);
+ static bool removeCaptureDevice(const std::string& deviceName);
+ // Enumerate available displays and return an id of the internal display
+ static uint64_t enumerateDisplays();
+
+ static ActiveDisplays& mutableActiveDisplays();
+
+ // NOTE: All members values are static so that all clients operate on the same state
+ // That is to say, this is effectively a singleton despite the fact that HIDL
+ // constructs a new instance for each client.
+ // Because our server has a single thread in the thread pool, these values are
+ // never accessed concurrently despite potentially having multiple instance objects
+ // using them.
+ static std::unordered_map<std::string, CameraRecord> sCameraList;
+ // Object destructs if client dies.
+ static std::mutex sLock; // Mutex on shared camera device list.
+ static std::condition_variable sCameraSignal; // Signal on camera device addition.
+ static std::unique_ptr<ConfigManager> sConfigManager; // ConfigManager
+ static std::shared_ptr<::aidl::android::frameworks::automotive::display::ICarDisplayProxy>
+ sDisplayProxy;
+ static std::unordered_map<uint8_t, uint64_t> sDisplayPortList;
+
+ uint64_t mInternalDisplayId;
+ std::shared_ptr<evs::IEvsEnumeratorStatusCallback> mCallback;
+};
+
+} // namespace aidl::android::hardware::automotive::evs::implementation
diff --git a/automotive/evs/aidl/impl/default/include/EvsGlDisplay.h b/automotive/evs/aidl/impl/default/include/EvsGlDisplay.h
new file mode 100644
index 0000000..ceabd9e
--- /dev/null
+++ b/automotive/evs/aidl/impl/default/include/EvsGlDisplay.h
@@ -0,0 +1,89 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include "GlWrapper.h"
+
+#include <aidl/android/frameworks/automotive/display/ICarDisplayProxy.h>
+#include <aidl/android/hardware/automotive/evs/BnEvsDisplay.h>
+#include <aidl/android/hardware/automotive/evs/BufferDesc.h>
+#include <aidl/android/hardware/automotive/evs/DisplayDesc.h>
+#include <aidl/android/hardware/automotive/evs/DisplayState.h>
+
+#include <thread>
+
+namespace aidl::android::hardware::automotive::evs::implementation {
+
+class EvsGlDisplay final : public BnEvsDisplay {
+ public:
+ // Methods from ::aidl::android::hardware::automotive::evs::IEvsDisplay
+ // follow.
+ ndk::ScopedAStatus getDisplayInfo(evs::DisplayDesc* _aidl_return) override;
+ ndk::ScopedAStatus getDisplayState(evs::DisplayState* _aidl_return) override;
+ ndk::ScopedAStatus getTargetBuffer(evs::BufferDesc* _aidl_return) override;
+ ndk::ScopedAStatus returnTargetBufferForDisplay(const evs::BufferDesc& buffer) override;
+ ndk::ScopedAStatus setDisplayState(evs::DisplayState state) override;
+
+ // Implementation details
+ EvsGlDisplay(const std::shared_ptr<automotivedisplay::ICarDisplayProxy>& service,
+ uint64_t displayId);
+ virtual ~EvsGlDisplay() override;
+
+ // This gets called if another caller "steals" ownership of the display
+ void forceShutdown();
+
+ private:
+ // A graphics buffer into which we'll store images. This member variable
+ // will be protected by semaphores.
+ struct BufferRecord {
+ ::aidl::android::hardware::graphics::common::HardwareBufferDescription description;
+ buffer_handle_t handle;
+ int fingerprint;
+ } mBuffer;
+
+ // State of a rendering thread
+ enum RenderThreadStates {
+ STOPPED = 0,
+ STOPPING = 1,
+ RUN = 2,
+ };
+
+ uint64_t mDisplayId;
+ evs::DisplayDesc mInfo;
+ evs::DisplayState mRequestedState GUARDED_BY(mLock) = evs::DisplayState::NOT_VISIBLE;
+ std::shared_ptr<automotivedisplay::ICarDisplayProxy> mDisplayProxy;
+
+ GlWrapper mGlWrapper;
+ mutable std::mutex mLock;
+
+ // This tells us whether or not our buffer is in use. Protected by
+ // semaphores.
+ bool mBufferBusy = false;
+
+ // Variables to synchronize a rendering thread w/ main and binder threads
+ std::thread mRenderThread;
+ RenderThreadStates mState GUARDED_BY(mLock) = STOPPED;
+ bool mBufferReady = false;
+ void renderFrames();
+ bool initializeGlContextLocked() REQUIRES(mLock);
+
+ std::condition_variable mBufferReadyToUse;
+ std::condition_variable mBufferReadyToRender;
+ std::condition_variable mBufferDone;
+};
+
+} // namespace aidl::android::hardware::automotive::evs::implementation
diff --git a/automotive/evs/aidl/impl/default/include/EvsMockCamera.h b/automotive/evs/aidl/impl/default/include/EvsMockCamera.h
new file mode 100644
index 0000000..7e010a2
--- /dev/null
+++ b/automotive/evs/aidl/impl/default/include/EvsMockCamera.h
@@ -0,0 +1,166 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include "ConfigManager.h"
+
+#include <aidl/android/hardware/automotive/evs/BnEvsCamera.h>
+#include <aidl/android/hardware/automotive/evs/BufferDesc.h>
+#include <aidl/android/hardware/automotive/evs/CameraDesc.h>
+#include <aidl/android/hardware/automotive/evs/CameraParam.h>
+#include <aidl/android/hardware/automotive/evs/EvsResult.h>
+#include <aidl/android/hardware/automotive/evs/IEvsCameraStream.h>
+#include <aidl/android/hardware/automotive/evs/IEvsDisplay.h>
+#include <aidl/android/hardware/automotive/evs/ParameterRange.h>
+#include <aidl/android/hardware/automotive/evs/Stream.h>
+// #include <android-base/result.h>
+#include <android/hardware_buffer.h>
+#include <ui/GraphicBuffer.h>
+
+#include <functional>
+#include <thread>
+
+namespace aidl::android::hardware::automotive::evs::implementation {
+
+class EvsMockCamera : public evs::BnEvsCamera {
+ // This prevents constructors from direct access while it allows this class to
+ // be instantiated via ndk::SharedRefBase::make<>.
+ private:
+ struct Sigil {
+ explicit Sigil() = default;
+ };
+
+ public:
+ // Methods from ::android::hardware::automotive::evs::IEvsCamera follow.
+ ndk::ScopedAStatus doneWithFrame(const std::vector<evs::BufferDesc>& buffers) override;
+ ndk::ScopedAStatus forcePrimaryClient(
+ const std::shared_ptr<evs::IEvsDisplay>& display) override;
+ ndk::ScopedAStatus getCameraInfo(evs::CameraDesc* _aidl_return) override;
+ ndk::ScopedAStatus getExtendedInfo(int32_t opaqueIdentifier,
+ std::vector<uint8_t>* value) override;
+ ndk::ScopedAStatus getIntParameter(evs::CameraParam id, std::vector<int32_t>* value) override;
+ ndk::ScopedAStatus getIntParameterRange(evs::CameraParam id,
+ evs::ParameterRange* _aidl_return) override;
+ ndk::ScopedAStatus getParameterList(std::vector<evs::CameraParam>* _aidl_return) override;
+ ndk::ScopedAStatus getPhysicalCameraInfo(const std::string& deviceId,
+ evs::CameraDesc* _aidl_return) override;
+ ndk::ScopedAStatus importExternalBuffers(const std::vector<evs::BufferDesc>& buffers,
+ int32_t* _aidl_return) override;
+ ndk::ScopedAStatus pauseVideoStream() override;
+ ndk::ScopedAStatus resumeVideoStream() override;
+ ndk::ScopedAStatus setExtendedInfo(int32_t opaqueIdentifier,
+ const std::vector<uint8_t>& opaqueValue) override;
+ ndk::ScopedAStatus setIntParameter(evs::CameraParam id, int32_t value,
+ std::vector<int32_t>* effectiveValue) override;
+ ndk::ScopedAStatus setPrimaryClient() override;
+ ndk::ScopedAStatus setMaxFramesInFlight(int32_t bufferCount) override;
+ ndk::ScopedAStatus startVideoStream(
+ const std::shared_ptr<evs::IEvsCameraStream>& receiver) override;
+ ndk::ScopedAStatus stopVideoStream() override;
+ ndk::ScopedAStatus unsetPrimaryClient() override;
+
+ static std::shared_ptr<EvsMockCamera> Create(const char* deviceName);
+ static std::shared_ptr<EvsMockCamera> Create(
+ const char* deviceName, std::unique_ptr<ConfigManager::CameraInfo>& camInfo,
+ const evs::Stream* streamCfg = nullptr);
+ EvsMockCamera(const EvsMockCamera&) = delete;
+ EvsMockCamera& operator=(const EvsMockCamera&) = delete;
+
+ virtual ~EvsMockCamera() override;
+ void shutdown();
+
+ const evs::CameraDesc& getDesc() { return mDescription; }
+
+ // Constructors
+ EvsMockCamera(Sigil sigil, const char* deviceName,
+ std::unique_ptr<ConfigManager::CameraInfo>& camInfo);
+
+ private:
+ // These three functions are expected to be called while mAccessLock is held
+ bool setAvailableFrames_Locked(unsigned bufferCount);
+ unsigned increaseAvailableFrames_Locked(unsigned numToAdd);
+ unsigned decreaseAvailableFrames_Locked(unsigned numToRemove);
+
+ void generateFrames();
+ void fillMockFrame(buffer_handle_t handle, const AHardwareBuffer_Desc* pDesc);
+ void returnBufferLocked(const uint32_t bufferId);
+ ndk::ScopedAStatus stopVideoStream_impl();
+
+ CameraDesc mDescription = {}; // The properties of this camera
+
+ std::thread mCaptureThread; // The thread we'll use to synthesize frames
+
+ // The callback used to deliver each frame
+ std::shared_ptr<evs::IEvsCameraStream> mStream;
+
+ // Horizontal pixel count in the buffers
+ uint32_t mWidth = 0;
+ // Vertical pixel count in the buffers
+ uint32_t mHeight = 0;
+ // Values from android_pixel_format_t
+ uint32_t mFormat = HAL_PIXEL_FORMAT_RGBA_8888;
+ // Values from from Gralloc.h
+ uint64_t mUsage =
+ GRALLOC_USAGE_HW_TEXTURE | GRALLOC_USAGE_SW_READ_RARELY | GRALLOC_USAGE_SW_WRITE_OFTEN;
+ // Bytes per line in the buffers
+ uint32_t mStride = 0;
+
+ struct BufferRecord {
+ buffer_handle_t handle;
+ bool inUse;
+
+ explicit BufferRecord(buffer_handle_t h) : handle(h), inUse(false){};
+ };
+
+ std::vector<BufferRecord> mBuffers; // Graphics buffers to transfer images
+ unsigned mFramesAllowed; // How many buffers are we currently using
+ unsigned mFramesInUse; // How many buffers are currently outstanding
+
+ enum StreamStateValues {
+ STOPPED,
+ RUNNING,
+ STOPPING,
+ DEAD,
+ };
+ StreamStateValues mStreamState;
+
+ // Synchronization necessary to deconflict mCaptureThread from the main service thread
+ std::mutex mAccessLock;
+
+ // Static camera module information
+ std::unique_ptr<ConfigManager::CameraInfo>& mCameraInfo;
+
+ // For the extended info
+ std::unordered_map<uint32_t, std::vector<uint8_t>> mExtInfo;
+
+ // For the camera parameters.
+ struct CameraParameterDesc {
+ CameraParameterDesc(int min = 0, int max = 0, int step = 0, int value = 0) {
+ this->range.min = min;
+ this->range.max = max;
+ this->range.step = step;
+ this->value = value;
+ }
+
+ ParameterRange range;
+ int32_t value;
+ };
+ std::unordered_map<CameraParam, std::shared_ptr<CameraParameterDesc>> mParams;
+ void initializeParameters();
+};
+
+} // namespace aidl::android::hardware::automotive::evs::implementation
diff --git a/automotive/evs/aidl/impl/default/include/GlWrapper.h b/automotive/evs/aidl/impl/default/include/GlWrapper.h
new file mode 100644
index 0000000..adb250c
--- /dev/null
+++ b/automotive/evs/aidl/impl/default/include/GlWrapper.h
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <EGL/egl.h>
+#include <EGL/eglext.h>
+#include <GLES2/gl2.h>
+#include <GLES2/gl2ext.h>
+#include <GLES3/gl3.h>
+#include <GLES3/gl3ext.h>
+#include <aidl/android/frameworks/automotive/display/ICarDisplayProxy.h>
+#include <aidl/android/hardware/automotive/evs/BufferDesc.h>
+#include <android-base/logging.h>
+#include <bufferqueueconverter/BufferQueueConverter.h>
+
+namespace aidl::android::hardware::automotive::evs::implementation {
+
+namespace automotivedisplay = ::aidl::android::frameworks::automotive::display;
+
+class GlWrapper {
+ public:
+ GlWrapper() : mSurfaceHolder(::android::SurfaceHolderUniquePtr(nullptr, nullptr)) {}
+ bool initialize(const std::shared_ptr<automotivedisplay::ICarDisplayProxy>& svc,
+ uint64_t displayId);
+ void shutdown();
+
+ bool updateImageTexture(
+ buffer_handle_t handle,
+ const ::aidl::android::hardware::graphics::common::HardwareBufferDescription&
+ description);
+ void renderImageToScreen();
+
+ void showWindow(const std::shared_ptr<automotivedisplay::ICarDisplayProxy>& svc,
+ uint64_t displayId);
+ void hideWindow(const std::shared_ptr<automotivedisplay::ICarDisplayProxy>& svc,
+ uint64_t displayId);
+
+ unsigned getWidth() { return mWidth; };
+ unsigned getHeight() { return mHeight; };
+
+ private:
+ ::android::sp<::android::hardware::graphics::bufferqueue::V2_0::IGraphicBufferProducer>
+ mGfxBufferProducer;
+
+ EGLDisplay mDisplay;
+ EGLSurface mSurface;
+ EGLContext mContext;
+
+ unsigned mWidth = 0;
+ unsigned mHeight = 0;
+
+ EGLImageKHR mKHRimage = EGL_NO_IMAGE_KHR;
+
+ GLuint mTextureMap = 0;
+ GLuint mShaderProgram = 0;
+
+ // Opaque handle for a native hardware buffer defined in
+ // frameworks/native/opengl/include/EGL/eglplatform.h
+ ANativeWindow* mWindow;
+
+ // Pointer to a Surface wrapper.
+ ::android::SurfaceHolderUniquePtr mSurfaceHolder;
+};
+
+} // namespace aidl::android::hardware::automotive::evs::implementation
diff --git a/automotive/evs/aidl/impl/default/manifest_evs-default-service.xml b/automotive/evs/aidl/impl/default/manifest_evs-default-service.xml
new file mode 100644
index 0000000..50692f7
--- /dev/null
+++ b/automotive/evs/aidl/impl/default/manifest_evs-default-service.xml
@@ -0,0 +1,7 @@
+<manifest version="1.0" type="device">
+ <hal format="aidl">
+ <name>android.hardware.automotive.evs</name>
+ <fqname>IEvsEnumerator/hw/0</fqname>
+ <version>1</version>
+ </hal>
+</manifest>
diff --git a/automotive/evs/aidl/impl/default/resources/evs_mock_configuration.xml b/automotive/evs/aidl/impl/default/resources/evs_mock_configuration.xml
new file mode 100644
index 0000000..6cbc18e
--- /dev/null
+++ b/automotive/evs/aidl/impl/default/resources/evs_mock_configuration.xml
@@ -0,0 +1,68 @@
+<?xml version='1.0' encoding='utf-8'?>
+<!-- Copyright (C) 2019 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<!-- Exterior View System Example Configuration
+
+ Android Automotive axes are used to define coordinates.
+ See https://source.android.com/devices/sensors/sensor-types#auto_axes
+
+ Use evs_configuration.dtd with xmllint tool, to validate XML configuration file
+-->
+
+<configuration>
+ <!-- system configuration -->
+ <system>
+ <!-- number of cameras available to EVS -->
+ <num_cameras value='1'/>
+ </system>
+
+ <!-- camera information -->
+ <camera>
+ <!-- camera device starts -->
+ <device id='/dev/video10' position='rear'>
+ <caps>
+ <!-- list of supported controls -->
+ <supported_controls>
+ <control name='BRIGHTNESS' min='0' max='255'/>
+ <control name='CONTRAST' min='0' max='255'/>
+ </supported_controls>
+
+ <stream id='0' width='640' height='360' format='RGBA_8888' framerate='30'/>
+ </caps>
+
+ <!-- list of parameters -->
+ <characteristics>
+ <!-- Camera intrinsic calibration matrix. See
+ https://developer.android.com/reference/android/hardware/camera2/CameraCharacteristics.html#LENS_INTRINSIC_CALIBRATION
+ -->
+ <parameter
+ name='LENS_INTRINSIC_CALIBRATION'
+ type='float'
+ size='5'
+ value='0.0,0.0,0.0,0.0,0.0'
+ />
+ </characteristics>
+ </device>
+ </camera>
+ <display>
+ <device id='display0' position='driver'>
+ <caps>
+ <!-- list of supported inpu stream configurations -->
+ <stream id='0' width='1280' height='720' format='RGBA_8888' framerate='30'/>
+ </caps>
+ </device>
+ </display>
+</configuration>
+
diff --git a/automotive/evs/aidl/impl/default/src/ConfigManager.cpp b/automotive/evs/aidl/impl/default/src/ConfigManager.cpp
new file mode 100644
index 0000000..da791ed
--- /dev/null
+++ b/automotive/evs/aidl/impl/default/src/ConfigManager.cpp
@@ -0,0 +1,992 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "ConfigManager.h"
+
+#include <android-base/parseint.h>
+#include <hardware/gralloc.h>
+#include <utils/SystemClock.h>
+
+#include <fstream>
+#include <sstream>
+#include <string_view>
+#include <thread>
+
+namespace {
+
+using ::aidl::android::hardware::automotive::evs::CameraParam;
+using ::aidl::android::hardware::graphics::common::PixelFormat;
+using ::tinyxml2::XMLAttribute;
+using ::tinyxml2::XMLDocument;
+using ::tinyxml2::XMLElement;
+
+} // namespace
+
+std::string_view ConfigManager::sConfigDefaultPath =
+ "/vendor/etc/automotive/evs/evs_mock_hal_configuration.xml";
+std::string_view ConfigManager::sConfigOverridePath =
+ "/vendor/etc/automotive/evs/evs_configuration_override.xml";
+
+void ConfigManager::printElementNames(const XMLElement* rootElem, const std::string& prefix) const {
+ const XMLElement* curElem = rootElem;
+
+ while (curElem != nullptr) {
+ LOG(VERBOSE) << "[ELEM] " << prefix << curElem->Name();
+ const XMLAttribute* curAttr = curElem->FirstAttribute();
+ while (curAttr) {
+ LOG(VERBOSE) << "[ATTR] " << prefix << curAttr->Name() << ": " << curAttr->Value();
+ curAttr = curAttr->Next();
+ }
+
+ /* recursively go down to descendants */
+ printElementNames(curElem->FirstChildElement(), prefix + "\t");
+
+ curElem = curElem->NextSiblingElement();
+ }
+}
+
+void ConfigManager::readCameraInfo(const XMLElement* const aCameraElem) {
+ if (aCameraElem == nullptr) {
+ LOG(WARNING) << "XML file does not have required camera element";
+ return;
+ }
+
+ const XMLElement* curElem = aCameraElem->FirstChildElement();
+ while (curElem != nullptr) {
+ if (!strcmp(curElem->Name(), "group")) {
+ /* camera group identifier */
+ const char* id = curElem->FindAttribute("id")->Value();
+
+ /* create a camera group to be filled */
+ CameraGroupInfo* aCamera = new CameraGroupInfo();
+
+ /* read camera device information */
+ if (!readCameraDeviceInfo(aCamera, curElem)) {
+ LOG(WARNING) << "Failed to read a camera information of " << id;
+ delete aCamera;
+ continue;
+ }
+
+ /* camera group synchronization */
+ const char* sync = curElem->FindAttribute("synchronized")->Value();
+ if (!strcmp(sync, "CALIBRATED")) {
+ aCamera->synchronized = ANDROID_LOGICAL_MULTI_CAMERA_SENSOR_SYNC_TYPE_CALIBRATED;
+ } else if (!strcmp(sync, "APPROXIMATE")) {
+ aCamera->synchronized = ANDROID_LOGICAL_MULTI_CAMERA_SENSOR_SYNC_TYPE_APPROXIMATE;
+ } else {
+ aCamera->synchronized = 0; // Not synchronized
+ }
+
+ /* add a group to hash map */
+ mCameraGroups.insert_or_assign(id, std::unique_ptr<CameraGroupInfo>(aCamera));
+ } else if (!std::strcmp(curElem->Name(), "device")) {
+ /* camera unique identifier */
+ const char* id = curElem->FindAttribute("id")->Value();
+
+ /* camera mount location */
+ const char* pos = curElem->FindAttribute("position")->Value();
+
+ /* create a camera device to be filled */
+ CameraInfo* aCamera = new CameraInfo();
+
+ /* read camera device information */
+ if (!readCameraDeviceInfo(aCamera, curElem)) {
+ LOG(WARNING) << "Failed to read a camera information of " << id;
+ delete aCamera;
+ continue;
+ }
+
+ /* store read camera module information */
+ mCameraInfo.insert_or_assign(id, std::unique_ptr<CameraInfo>(aCamera));
+
+ /* assign a camera device to a position group */
+ mCameraPosition[pos].insert(id);
+ } else {
+ /* ignore other device types */
+ LOG(DEBUG) << "Unknown element " << curElem->Name() << " is ignored";
+ }
+
+ curElem = curElem->NextSiblingElement();
+ }
+}
+
+bool ConfigManager::readCameraDeviceInfo(CameraInfo* aCamera, const XMLElement* aDeviceElem) {
+ if (aCamera == nullptr || aDeviceElem == nullptr) {
+ return false;
+ }
+
+ /* size information to allocate camera_metadata_t */
+ size_t totalEntries = 0;
+ size_t totalDataSize = 0;
+
+ /* read device capabilities */
+ totalEntries +=
+ readCameraCapabilities(aDeviceElem->FirstChildElement("caps"), aCamera, totalDataSize);
+
+ /* read camera metadata */
+ totalEntries += readCameraMetadata(aDeviceElem->FirstChildElement("characteristics"), aCamera,
+ totalDataSize);
+
+ /* construct camera_metadata_t */
+ if (!constructCameraMetadata(aCamera, totalEntries, totalDataSize)) {
+ LOG(WARNING) << "Either failed to allocate memory or "
+ << "allocated memory was not large enough";
+ }
+
+ return true;
+}
+
+size_t ConfigManager::readCameraCapabilities(const XMLElement* const aCapElem, CameraInfo* aCamera,
+ size_t& dataSize) {
+ if (aCapElem == nullptr || aCamera == nullptr) {
+ return 0;
+ }
+
+ std::string token;
+ const XMLElement* curElem = nullptr;
+
+ /* a list of supported camera parameters/controls */
+ curElem = aCapElem->FirstChildElement("supported_controls");
+ if (curElem != nullptr) {
+ const XMLElement* ctrlElem = curElem->FirstChildElement("control");
+ while (ctrlElem != nullptr) {
+ const char* nameAttr = ctrlElem->FindAttribute("name")->Value();
+ int32_t minVal = INT32_MIN, maxVal = INT32_MAX;
+ if (!android::base::ParseInt(ctrlElem->FindAttribute("min")->Value(), &minVal)) {
+ LOG(WARNING) << "Failed to parse " << ctrlElem->FindAttribute("min")->Value();
+ }
+
+ if (!android::base::ParseInt(ctrlElem->FindAttribute("max")->Value(), &maxVal)) {
+ LOG(WARNING) << "Failed to parse " << ctrlElem->FindAttribute("max")->Value();
+ }
+
+ int32_t stepVal = 1;
+ const XMLAttribute* stepAttr = ctrlElem->FindAttribute("step");
+ if (stepAttr != nullptr) {
+ if (!android::base::ParseInt(stepAttr->Value(), &stepVal)) {
+ LOG(WARNING) << "Failed to parse " << stepAttr->Value();
+ }
+ }
+
+ CameraParam aParam;
+ if (ConfigManagerUtil::convertToEvsCameraParam(nameAttr, aParam)) {
+ aCamera->controls.insert_or_assign(
+ aParam, std::move(std::make_tuple(minVal, maxVal, stepVal)));
+ }
+
+ ctrlElem = ctrlElem->NextSiblingElement("control");
+ }
+ }
+
+ /* a list of camera stream configurations */
+ curElem = aCapElem->FirstChildElement("stream");
+ while (curElem != nullptr) {
+ /* read 5 attributes */
+ const XMLAttribute* idAttr = curElem->FindAttribute("id");
+ const XMLAttribute* widthAttr = curElem->FindAttribute("width");
+ const XMLAttribute* heightAttr = curElem->FindAttribute("height");
+ const XMLAttribute* fmtAttr = curElem->FindAttribute("format");
+ const XMLAttribute* fpsAttr = curElem->FindAttribute("framerate");
+
+ int32_t id = -1;
+ int32_t framerate = 0;
+ if (!android::base::ParseInt(idAttr->Value(), &id)) {
+ LOG(WARNING) << "Failed to parse " << idAttr->Value();
+ }
+ if (fpsAttr != nullptr) {
+ if (!android::base::ParseInt(fpsAttr->Value(), &framerate)) {
+ LOG(WARNING) << "Failed to parse " << fpsAttr->Value();
+ }
+ }
+
+ PixelFormat format = PixelFormat::UNSPECIFIED;
+ if (ConfigManagerUtil::convertToPixelFormat(fmtAttr->Value(), format)) {
+ StreamConfiguration cfg = {
+ .id = id,
+ .format = format,
+ .type = ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS_OUTPUT,
+ .framerate = framerate,
+ };
+
+ if (!android::base::ParseInt(widthAttr->Value(), &cfg.width) ||
+ !android::base::ParseInt(heightAttr->Value(), &cfg.height)) {
+ LOG(WARNING) << "Failed to parse " << widthAttr->Value() << " and "
+ << heightAttr->Value();
+ }
+ aCamera->streamConfigurations.insert_or_assign(id, cfg);
+ }
+
+ curElem = curElem->NextSiblingElement("stream");
+ }
+
+ dataSize = calculate_camera_metadata_entry_data_size(
+ get_camera_metadata_tag_type(ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS),
+ aCamera->streamConfigurations.size() * sizeof(StreamConfiguration));
+
+ /* a single camera metadata entry contains multiple stream configurations */
+ return dataSize > 0 ? 1 : 0;
+}
+
+size_t ConfigManager::readCameraMetadata(const XMLElement* const aParamElem, CameraInfo* aCamera,
+ size_t& dataSize) {
+ if (aParamElem == nullptr || aCamera == nullptr) {
+ return 0;
+ }
+
+ const XMLElement* curElem = aParamElem->FirstChildElement("parameter");
+ size_t numEntries = 0;
+ camera_metadata_tag_t tag;
+ while (curElem != nullptr) {
+ if (ConfigManagerUtil::convertToMetadataTag(curElem->FindAttribute("name")->Value(), tag)) {
+ switch (tag) {
+ case ANDROID_LENS_DISTORTION:
+ case ANDROID_LENS_POSE_ROTATION:
+ case ANDROID_LENS_POSE_TRANSLATION:
+ case ANDROID_LENS_INTRINSIC_CALIBRATION: {
+ /* float[] */
+ size_t count = 0;
+ void* data = ConfigManagerUtil::convertFloatArray(
+ curElem->FindAttribute("size")->Value(),
+ curElem->FindAttribute("value")->Value(), count);
+
+ aCamera->cameraMetadata.insert_or_assign(tag, std::make_pair(data, count));
+
+ ++numEntries;
+ dataSize += calculate_camera_metadata_entry_data_size(
+ get_camera_metadata_tag_type(tag), count);
+
+ break;
+ }
+
+ case ANDROID_REQUEST_AVAILABLE_CAPABILITIES: {
+ camera_metadata_enum_android_request_available_capabilities_t* data =
+ new camera_metadata_enum_android_request_available_capabilities_t[1];
+ if (ConfigManagerUtil::convertToCameraCapability(
+ curElem->FindAttribute("value")->Value(), *data)) {
+ aCamera->cameraMetadata.insert_or_assign(tag,
+ std::make_pair((void*)data, 1));
+
+ ++numEntries;
+ dataSize += calculate_camera_metadata_entry_data_size(
+ get_camera_metadata_tag_type(tag), 1);
+ }
+ break;
+ }
+
+ case ANDROID_LOGICAL_MULTI_CAMERA_PHYSICAL_IDS: {
+ /* a comma-separated list of physical camera devices */
+ size_t len = strlen(curElem->FindAttribute("value")->Value());
+ char* data = new char[len + 1];
+ memcpy(data, curElem->FindAttribute("value")->Value(), len * sizeof(char));
+
+ /* replace commas with null char */
+ char* p = data;
+ while (*p != '\0') {
+ if (*p == ',') {
+ *p = '\0';
+ }
+ ++p;
+ }
+
+ aCamera->cameraMetadata.insert_or_assign(tag,
+ std::make_pair((void*)data, len + 1));
+
+ ++numEntries;
+ dataSize += calculate_camera_metadata_entry_data_size(
+ get_camera_metadata_tag_type(tag), len);
+ break;
+ }
+
+ /* TODO(b/140416878): add vendor-defined/custom tag support */
+ default:
+ LOG(WARNING) << "Parameter " << curElem->FindAttribute("name")->Value()
+ << " is not supported";
+ break;
+ }
+ } else {
+ LOG(WARNING) << "Unsupported metadata tag " << curElem->FindAttribute("name")->Value()
+ << " is found.";
+ }
+
+ curElem = curElem->NextSiblingElement("parameter");
+ }
+
+ return numEntries;
+}
+
+bool ConfigManager::constructCameraMetadata(CameraInfo* aCamera, size_t totalEntries,
+ size_t totalDataSize) {
+ if (aCamera == nullptr || !aCamera->allocate(totalEntries, totalDataSize)) {
+ LOG(ERROR) << "Failed to allocate memory for camera metadata";
+ return false;
+ }
+
+ const size_t numStreamConfigs = aCamera->streamConfigurations.size();
+ std::unique_ptr<int32_t[]> data(new int32_t[sizeof(StreamConfiguration) * numStreamConfigs]);
+ int32_t* ptr = data.get();
+ for (auto& cfg : aCamera->streamConfigurations) {
+ memcpy(ptr, &cfg.second, sizeof(StreamConfiguration));
+ ptr += sizeof(StreamConfiguration);
+ }
+ int32_t err = add_camera_metadata_entry(
+ aCamera->characteristics, ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS, data.get(),
+ numStreamConfigs * sizeof(StreamConfiguration));
+
+ if (err) {
+ LOG(ERROR) << "Failed to add stream configurations to metadata, ignored";
+ return false;
+ }
+
+ bool success = true;
+ for (auto& [tag, entry] : aCamera->cameraMetadata) {
+ /* try to add new camera metadata entry */
+ int32_t err =
+ add_camera_metadata_entry(aCamera->characteristics, tag, entry.first, entry.second);
+ if (err) {
+ LOG(ERROR) << "Failed to add an entry with a tag, " << std::hex << tag;
+
+ /* may exceed preallocated capacity */
+ LOG(ERROR) << "Camera metadata has "
+ << get_camera_metadata_entry_count(aCamera->characteristics) << " / "
+ << get_camera_metadata_entry_capacity(aCamera->characteristics)
+ << " entries and "
+ << get_camera_metadata_data_count(aCamera->characteristics) << " / "
+ << get_camera_metadata_data_capacity(aCamera->characteristics)
+ << " bytes are filled.";
+ LOG(ERROR) << "\tCurrent metadata entry requires "
+ << calculate_camera_metadata_entry_data_size(tag, entry.second) << " bytes.";
+
+ success = false;
+ }
+ }
+
+ LOG(VERBOSE) << "Camera metadata has "
+ << get_camera_metadata_entry_count(aCamera->characteristics) << " / "
+ << get_camera_metadata_entry_capacity(aCamera->characteristics) << " entries and "
+ << get_camera_metadata_data_count(aCamera->characteristics) << " / "
+ << get_camera_metadata_data_capacity(aCamera->characteristics)
+ << " bytes are filled.";
+ return success;
+}
+
+void ConfigManager::readSystemInfo(const XMLElement* const aSysElem) {
+ if (aSysElem == nullptr) {
+ return;
+ }
+
+ /*
+ * Please note that this function assumes that a given system XML element
+ * and its child elements follow DTD. If it does not, it will cause a
+ * segmentation fault due to the failure of finding expected attributes.
+ */
+
+ /* read number of cameras available in the system */
+ const XMLElement* xmlElem = aSysElem->FirstChildElement("num_cameras");
+ if (xmlElem != nullptr) {
+ if (!android::base::ParseInt(xmlElem->FindAttribute("value")->Value(),
+ &mSystemInfo.numCameras)) {
+ LOG(WARNING) << "Failed to parse " << xmlElem->FindAttribute("value")->Value();
+ }
+ }
+}
+
+void ConfigManager::readDisplayInfo(const XMLElement* const aDisplayElem) {
+ if (aDisplayElem == nullptr) {
+ LOG(WARNING) << "XML file does not have required camera element";
+ return;
+ }
+
+ const XMLElement* curDev = aDisplayElem->FirstChildElement("device");
+ while (curDev != nullptr) {
+ const char* id = curDev->FindAttribute("id")->Value();
+ std::unique_ptr<DisplayInfo> dpy(new DisplayInfo());
+ if (dpy == nullptr) {
+ LOG(ERROR) << "Failed to allocate memory for DisplayInfo";
+ return;
+ }
+
+ const XMLElement* cap = curDev->FirstChildElement("caps");
+ if (cap != nullptr) {
+ const XMLElement* curStream = cap->FirstChildElement("stream");
+ while (curStream != nullptr) {
+ /* read 4 attributes */
+ const XMLAttribute* idAttr = curStream->FindAttribute("id");
+ const XMLAttribute* widthAttr = curStream->FindAttribute("width");
+ const XMLAttribute* heightAttr = curStream->FindAttribute("height");
+ const XMLAttribute* fmtAttr = curStream->FindAttribute("format");
+
+ int32_t id = -1;
+ if (!android::base::ParseInt(idAttr->Value(), &id)) {
+ LOG(WARNING) << "Failed to parse " << idAttr->Value();
+ }
+ PixelFormat format = PixelFormat::UNSPECIFIED;
+ if (ConfigManagerUtil::convertToPixelFormat(fmtAttr->Value(), format)) {
+ StreamConfiguration cfg = {
+ .id = id,
+ .format = format,
+ .type = ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS_INPUT,
+ };
+ if (!android::base::ParseInt(widthAttr->Value(), &cfg.width) ||
+ !android::base::ParseInt(heightAttr->Value(), &cfg.height)) {
+ LOG(WARNING) << "Failed to parse " << widthAttr->Value() << " and "
+ << heightAttr->Value();
+ }
+ dpy->streamConfigurations.insert_or_assign(id, cfg);
+ }
+
+ curStream = curStream->NextSiblingElement("stream");
+ }
+ }
+
+ mDisplayInfo.insert_or_assign(id, std::move(dpy));
+ curDev = curDev->NextSiblingElement("device");
+ }
+
+ return;
+}
+
+bool ConfigManager::readConfigDataFromXML() noexcept {
+ XMLDocument xmlDoc;
+
+ const int64_t parsingStart = android::elapsedRealtimeNano();
+
+ /* load and parse a configuration file */
+ xmlDoc.LoadFile(sConfigOverridePath.data());
+ if (xmlDoc.ErrorID() != tinyxml2::XML_SUCCESS) {
+ xmlDoc.LoadFile(sConfigDefaultPath.data());
+ if (xmlDoc.ErrorID() != tinyxml2::XML_SUCCESS) {
+ LOG(ERROR) << "Failed to load and/or parse a configuration file, " << xmlDoc.ErrorStr();
+ return false;
+ }
+ }
+
+ /* retrieve the root element */
+ const XMLElement* rootElem = xmlDoc.RootElement();
+ if (std::strcmp(rootElem->Name(), "configuration") != 0) {
+ LOG(ERROR) << "A configuration file is not in the required format. "
+ << "See /etc/automotive/evs/evs_configuration.dtd";
+ return false;
+ }
+
+ std::unique_lock<std::mutex> lock(mConfigLock);
+
+ /*
+ * parse camera information; this needs to be done before reading system
+ * information
+ */
+ readCameraInfo(rootElem->FirstChildElement("camera"));
+
+ /* parse system information */
+ readSystemInfo(rootElem->FirstChildElement("system"));
+
+ /* parse display information */
+ readDisplayInfo(rootElem->FirstChildElement("display"));
+
+ /* configuration data is ready to be consumed */
+ mIsReady = true;
+
+ /* notify that configuration data is ready */
+ lock.unlock();
+ mConfigCond.notify_all();
+
+ const int64_t parsingEnd = android::elapsedRealtimeNano();
+ LOG(INFO) << "Parsing configuration file takes " << std::scientific
+ << (double)(parsingEnd - parsingStart) / 1000000.0 << " ms.";
+
+ return true;
+}
+
+bool ConfigManager::readConfigDataFromBinary() {
+ /* Temporary buffer to hold configuration data read from a binary file */
+ char mBuffer[1024];
+
+ std::fstream srcFile;
+ const int64_t readStart = android::elapsedRealtimeNano();
+
+ srcFile.open(mBinaryFilePath, std::fstream::in | std::fstream::binary);
+ if (!srcFile) {
+ LOG(ERROR) << "Failed to open a source binary file, " << mBinaryFilePath;
+ return false;
+ }
+
+ std::unique_lock<std::mutex> lock(mConfigLock);
+ mIsReady = false;
+
+ /* read configuration data into the internal buffer */
+ srcFile.read(mBuffer, sizeof(mBuffer));
+ LOG(VERBOSE) << __FUNCTION__ << ": " << srcFile.gcount() << " bytes are read.";
+ char* p = mBuffer;
+ size_t sz = 0;
+
+ /* read number of camera group information entries */
+ const size_t ngrps = *(reinterpret_cast<size_t*>(p));
+ p += sizeof(size_t);
+
+ /* read each camera information entry */
+ for (size_t cidx = 0; cidx < ngrps; ++cidx) {
+ /* read camera identifier */
+ std::string cameraId = *(reinterpret_cast<std::string*>(p));
+ p += sizeof(std::string);
+
+ /* size of camera_metadata_t */
+ const size_t num_entry = *(reinterpret_cast<size_t*>(p));
+ p += sizeof(size_t);
+ const size_t num_data = *(reinterpret_cast<size_t*>(p));
+ p += sizeof(size_t);
+
+ /* create CameraInfo and add it to hash map */
+ std::unique_ptr<ConfigManager::CameraGroupInfo> aCamera;
+ if (aCamera == nullptr || !aCamera->allocate(num_entry, num_data)) {
+ LOG(ERROR) << "Failed to create new CameraInfo object";
+ mCameraInfo.clear();
+ return false;
+ }
+
+ /* controls */
+ typedef struct {
+ CameraParam cid;
+ int32_t min;
+ int32_t max;
+ int32_t step;
+ } CameraCtrl;
+ sz = *(reinterpret_cast<size_t*>(p));
+ p += sizeof(size_t);
+ CameraCtrl* ptr = reinterpret_cast<CameraCtrl*>(p);
+ for (size_t idx = 0; idx < sz; ++idx) {
+ CameraCtrl temp = *ptr++;
+ aCamera->controls.insert_or_assign(
+ temp.cid, std::move(std::make_tuple(temp.min, temp.max, temp.step)));
+ }
+ p = reinterpret_cast<char*>(ptr);
+
+ /* stream configurations */
+ sz = *(reinterpret_cast<size_t*>(p));
+ p += sizeof(size_t);
+ int32_t* i32_ptr = reinterpret_cast<int32_t*>(p);
+ for (size_t idx = 0; idx < sz; ++idx) {
+ const int32_t id = *i32_ptr++;
+
+ StreamConfiguration temp;
+ memcpy(&temp, i32_ptr, sizeof(StreamConfiguration));
+ i32_ptr += sizeof(StreamConfiguration);
+ aCamera->streamConfigurations.insert_or_assign(id, temp);
+ }
+ p = reinterpret_cast<char*>(i32_ptr);
+
+ /* synchronization */
+ aCamera->synchronized = *(reinterpret_cast<int32_t*>(p));
+ p += sizeof(int32_t);
+
+ for (size_t idx = 0; idx < num_entry; ++idx) {
+ /* Read camera metadata entries */
+ camera_metadata_tag_t tag = *reinterpret_cast<camera_metadata_tag_t*>(p);
+ p += sizeof(camera_metadata_tag_t);
+ const size_t count = *reinterpret_cast<size_t*>(p);
+ p += sizeof(size_t);
+
+ const int32_t type = get_camera_metadata_tag_type(tag);
+ switch (type) {
+ case TYPE_BYTE: {
+ add_camera_metadata_entry(aCamera->characteristics, tag, p, count);
+ p += count * sizeof(uint8_t);
+ break;
+ }
+ case TYPE_INT32: {
+ add_camera_metadata_entry(aCamera->characteristics, tag, p, count);
+ p += count * sizeof(int32_t);
+ break;
+ }
+ case TYPE_FLOAT: {
+ add_camera_metadata_entry(aCamera->characteristics, tag, p, count);
+ p += count * sizeof(float);
+ break;
+ }
+ case TYPE_INT64: {
+ add_camera_metadata_entry(aCamera->characteristics, tag, p, count);
+ p += count * sizeof(int64_t);
+ break;
+ }
+ case TYPE_DOUBLE: {
+ add_camera_metadata_entry(aCamera->characteristics, tag, p, count);
+ p += count * sizeof(double);
+ break;
+ }
+ case TYPE_RATIONAL:
+ p += count * sizeof(camera_metadata_rational_t);
+ break;
+ default:
+ LOG(WARNING) << "Type " << type << " is unknown; "
+ << "data may be corrupted.";
+ break;
+ }
+ }
+
+ mCameraInfo.insert_or_assign(cameraId, std::move(aCamera));
+ }
+
+ /* read number of camera information entries */
+ const size_t ncams = *(reinterpret_cast<size_t*>(p));
+ p += sizeof(size_t);
+
+ /* read each camera information entry */
+ for (size_t cidx = 0; cidx < ncams; ++cidx) {
+ /* read camera identifier */
+ std::string cameraId = *(reinterpret_cast<std::string*>(p));
+ p += sizeof(std::string);
+
+ /* size of camera_metadata_t */
+ const size_t num_entry = *(reinterpret_cast<size_t*>(p));
+ p += sizeof(size_t);
+ const size_t num_data = *(reinterpret_cast<size_t*>(p));
+ p += sizeof(size_t);
+
+ /* create CameraInfo and add it to hash map */
+ std::unique_ptr<ConfigManager::CameraInfo> aCamera;
+ if (aCamera == nullptr || !aCamera->allocate(num_entry, num_data)) {
+ LOG(ERROR) << "Failed to create new CameraInfo object";
+ mCameraInfo.clear();
+ return false;
+ }
+
+ /* controls */
+ typedef struct {
+ CameraParam cid;
+ int32_t min;
+ int32_t max;
+ int32_t step;
+ } CameraCtrl;
+ sz = *(reinterpret_cast<size_t*>(p));
+ p += sizeof(size_t);
+ CameraCtrl* ptr = reinterpret_cast<CameraCtrl*>(p);
+ for (size_t idx = 0; idx < sz; ++idx) {
+ CameraCtrl temp = *ptr++;
+ aCamera->controls.insert_or_assign(
+ temp.cid, std::move(std::make_tuple(temp.min, temp.max, temp.step)));
+ }
+ p = reinterpret_cast<char*>(ptr);
+
+ /* stream configurations */
+ sz = *(reinterpret_cast<size_t*>(p));
+ p += sizeof(size_t);
+ int32_t* i32_ptr = reinterpret_cast<int32_t*>(p);
+ for (size_t idx = 0; idx < sz; ++idx) {
+ const int32_t id = *i32_ptr++;
+
+ StreamConfiguration temp;
+ memcpy(&temp, i32_ptr, sizeof(StreamConfiguration));
+ i32_ptr += sizeof(StreamConfiguration);
+ aCamera->streamConfigurations.insert_or_assign(id, temp);
+ }
+ p = reinterpret_cast<char*>(i32_ptr);
+
+ for (size_t idx = 0; idx < num_entry; ++idx) {
+ /* Read camera metadata entries */
+ camera_metadata_tag_t tag = *reinterpret_cast<camera_metadata_tag_t*>(p);
+ p += sizeof(camera_metadata_tag_t);
+ const size_t count = *reinterpret_cast<size_t*>(p);
+ p += sizeof(size_t);
+
+ const int32_t type = get_camera_metadata_tag_type(tag);
+ switch (type) {
+ case TYPE_BYTE: {
+ add_camera_metadata_entry(aCamera->characteristics, tag, p, count);
+ p += count * sizeof(uint8_t);
+ break;
+ }
+ case TYPE_INT32: {
+ add_camera_metadata_entry(aCamera->characteristics, tag, p, count);
+ p += count * sizeof(int32_t);
+ break;
+ }
+ case TYPE_FLOAT: {
+ add_camera_metadata_entry(aCamera->characteristics, tag, p, count);
+ p += count * sizeof(float);
+ break;
+ }
+ case TYPE_INT64: {
+ add_camera_metadata_entry(aCamera->characteristics, tag, p, count);
+ p += count * sizeof(int64_t);
+ break;
+ }
+ case TYPE_DOUBLE: {
+ add_camera_metadata_entry(aCamera->characteristics, tag, p, count);
+ p += count * sizeof(double);
+ break;
+ }
+ case TYPE_RATIONAL:
+ p += count * sizeof(camera_metadata_rational_t);
+ break;
+ default:
+ LOG(WARNING) << "Type " << type << " is unknown; "
+ << "data may be corrupted.";
+ break;
+ }
+ }
+
+ mCameraInfo.insert_or_assign(cameraId, std::move(aCamera));
+ }
+
+ mIsReady = true;
+
+ /* notify that configuration data is ready */
+ lock.unlock();
+ mConfigCond.notify_all();
+
+ int64_t readEnd = android::elapsedRealtimeNano();
+ LOG(INFO) << __FUNCTION__ << " takes " << std::scientific
+ << (double)(readEnd - readStart) / 1000000.0 << " ms.";
+
+ return true;
+}
+
+bool ConfigManager::writeConfigDataToBinary() {
+ std::fstream outFile;
+
+ const int64_t writeStart = android::elapsedRealtimeNano();
+
+ outFile.open(mBinaryFilePath, std::fstream::out | std::fstream::binary);
+ if (!outFile) {
+ LOG(ERROR) << "Failed to open a destination binary file, " << mBinaryFilePath;
+ return false;
+ }
+
+ /* lock a configuration data while it's being written to the filesystem */
+ std::lock_guard<std::mutex> lock(mConfigLock);
+
+ /* write camera group information */
+ size_t sz = mCameraGroups.size();
+ outFile.write(reinterpret_cast<const char*>(&sz), sizeof(size_t));
+ for (auto&& [camId, camInfo] : mCameraGroups) {
+ LOG(INFO) << "Storing camera group " << camId;
+
+ /* write a camera identifier string */
+ outFile.write(reinterpret_cast<const char*>(&camId), sizeof(std::string));
+
+ /* controls */
+ sz = camInfo->controls.size();
+ outFile.write(reinterpret_cast<const char*>(&sz), sizeof(size_t));
+ for (auto&& [ctrl, range] : camInfo->controls) {
+ outFile.write(reinterpret_cast<const char*>(&ctrl), sizeof(CameraParam));
+ outFile.write(reinterpret_cast<const char*>(&std::get<0>(range)), sizeof(int32_t));
+ outFile.write(reinterpret_cast<const char*>(&std::get<1>(range)), sizeof(int32_t));
+ outFile.write(reinterpret_cast<const char*>(&std::get<2>(range)), sizeof(int32_t));
+ }
+
+ /* stream configurations */
+ sz = camInfo->streamConfigurations.size();
+ outFile.write(reinterpret_cast<const char*>(&sz), sizeof(size_t));
+ for (auto&& [sid, cfg] : camInfo->streamConfigurations) {
+ outFile.write(reinterpret_cast<const char*>(sid), sizeof(int32_t));
+ outFile.write(reinterpret_cast<const char*>(&cfg), sizeof(cfg));
+ }
+
+ /* synchronization */
+ outFile.write(reinterpret_cast<const char*>(&camInfo->synchronized), sizeof(int32_t));
+
+ /* size of camera_metadata_t */
+ size_t num_entry = 0;
+ size_t num_data = 0;
+ if (camInfo->characteristics != nullptr) {
+ num_entry = get_camera_metadata_entry_count(camInfo->characteristics);
+ num_data = get_camera_metadata_data_count(camInfo->characteristics);
+ }
+ outFile.write(reinterpret_cast<const char*>(&num_entry), sizeof(size_t));
+ outFile.write(reinterpret_cast<const char*>(&num_data), sizeof(size_t));
+
+ /* write each camera metadata entry */
+ if (num_entry > 0) {
+ camera_metadata_entry_t entry;
+ for (size_t idx = 0; idx < num_entry; ++idx) {
+ if (get_camera_metadata_entry(camInfo->characteristics, idx, &entry)) {
+ LOG(ERROR) << "Failed to retrieve camera metadata entry " << idx;
+ outFile.close();
+ return false;
+ }
+
+ outFile.write(reinterpret_cast<const char*>(&entry.tag), sizeof(entry.tag));
+ outFile.write(reinterpret_cast<const char*>(&entry.count), sizeof(entry.count));
+
+ int32_t type = get_camera_metadata_tag_type(entry.tag);
+ switch (type) {
+ case TYPE_BYTE:
+ outFile.write(reinterpret_cast<const char*>(entry.data.u8),
+ sizeof(uint8_t) * entry.count);
+ break;
+ case TYPE_INT32:
+ outFile.write(reinterpret_cast<const char*>(entry.data.i32),
+ sizeof(int32_t) * entry.count);
+ break;
+ case TYPE_FLOAT:
+ outFile.write(reinterpret_cast<const char*>(entry.data.f),
+ sizeof(float) * entry.count);
+ break;
+ case TYPE_INT64:
+ outFile.write(reinterpret_cast<const char*>(entry.data.i64),
+ sizeof(int64_t) * entry.count);
+ break;
+ case TYPE_DOUBLE:
+ outFile.write(reinterpret_cast<const char*>(entry.data.d),
+ sizeof(double) * entry.count);
+ break;
+ case TYPE_RATIONAL:
+ [[fallthrough]];
+ default:
+ LOG(WARNING) << "Type " << type << " is not supported.";
+ break;
+ }
+ }
+ }
+ }
+
+ /* write camera device information */
+ sz = mCameraInfo.size();
+ outFile.write(reinterpret_cast<const char*>(&sz), sizeof(size_t));
+ for (auto&& [camId, camInfo] : mCameraInfo) {
+ LOG(INFO) << "Storing camera " << camId;
+
+ /* write a camera identifier string */
+ outFile.write(reinterpret_cast<const char*>(&camId), sizeof(std::string));
+
+ /* controls */
+ sz = camInfo->controls.size();
+ outFile.write(reinterpret_cast<const char*>(&sz), sizeof(size_t));
+ for (auto& [ctrl, range] : camInfo->controls) {
+ outFile.write(reinterpret_cast<const char*>(&ctrl), sizeof(CameraParam));
+ outFile.write(reinterpret_cast<const char*>(&std::get<0>(range)), sizeof(int32_t));
+ outFile.write(reinterpret_cast<const char*>(&std::get<1>(range)), sizeof(int32_t));
+ outFile.write(reinterpret_cast<const char*>(&std::get<2>(range)), sizeof(int32_t));
+ }
+
+ /* stream configurations */
+ sz = camInfo->streamConfigurations.size();
+ outFile.write(reinterpret_cast<const char*>(&sz), sizeof(size_t));
+ for (auto&& [sid, cfg] : camInfo->streamConfigurations) {
+ outFile.write(reinterpret_cast<const char*>(sid), sizeof(int32_t));
+ outFile.write(reinterpret_cast<const char*>(&cfg), sizeof(cfg));
+ }
+
+ /* size of camera_metadata_t */
+ size_t num_entry = 0;
+ size_t num_data = 0;
+ if (camInfo->characteristics != nullptr) {
+ num_entry = get_camera_metadata_entry_count(camInfo->characteristics);
+ num_data = get_camera_metadata_data_count(camInfo->characteristics);
+ }
+ outFile.write(reinterpret_cast<const char*>(&num_entry), sizeof(size_t));
+ outFile.write(reinterpret_cast<const char*>(&num_data), sizeof(size_t));
+
+ /* write each camera metadata entry */
+ if (num_entry > 0) {
+ camera_metadata_entry_t entry;
+ for (size_t idx = 0; idx < num_entry; ++idx) {
+ if (get_camera_metadata_entry(camInfo->characteristics, idx, &entry)) {
+ LOG(ERROR) << "Failed to retrieve camera metadata entry " << idx;
+ outFile.close();
+ return false;
+ }
+
+ outFile.write(reinterpret_cast<const char*>(&entry.tag), sizeof(entry.tag));
+ outFile.write(reinterpret_cast<const char*>(&entry.count), sizeof(entry.count));
+
+ int32_t type = get_camera_metadata_tag_type(entry.tag);
+ switch (type) {
+ case TYPE_BYTE:
+ outFile.write(reinterpret_cast<const char*>(entry.data.u8),
+ sizeof(uint8_t) * entry.count);
+ break;
+ case TYPE_INT32:
+ outFile.write(reinterpret_cast<const char*>(entry.data.i32),
+ sizeof(int32_t) * entry.count);
+ break;
+ case TYPE_FLOAT:
+ outFile.write(reinterpret_cast<const char*>(entry.data.f),
+ sizeof(float) * entry.count);
+ break;
+ case TYPE_INT64:
+ outFile.write(reinterpret_cast<const char*>(entry.data.i64),
+ sizeof(int64_t) * entry.count);
+ break;
+ case TYPE_DOUBLE:
+ outFile.write(reinterpret_cast<const char*>(entry.data.d),
+ sizeof(double) * entry.count);
+ break;
+ case TYPE_RATIONAL:
+ [[fallthrough]];
+ default:
+ LOG(WARNING) << "Type " << type << " is not supported.";
+ break;
+ }
+ }
+ }
+ }
+
+ outFile.close();
+ int64_t writeEnd = android::elapsedRealtimeNano();
+ LOG(INFO) << __FUNCTION__ << " takes " << std::scientific
+ << (double)(writeEnd - writeStart) / 1000000.0 << " ms.";
+
+ return true;
+}
+
+std::unique_ptr<ConfigManager> ConfigManager::Create() {
+ std::unique_ptr<ConfigManager> cfgMgr(new ConfigManager());
+
+ /*
+ * Read a configuration from XML file
+ *
+ * If this is too slow, ConfigManager::readConfigDataFromBinary() and
+ * ConfigManager::writeConfigDataToBinary()can serialize CameraInfo object
+ * to the filesystem and construct CameraInfo instead; this was
+ * evaluated as 10x faster.
+ */
+ if (!cfgMgr->readConfigDataFromXML()) {
+ return nullptr;
+ } else {
+ return cfgMgr;
+ }
+}
+
+ConfigManager::CameraInfo::~CameraInfo() {
+ free_camera_metadata(characteristics);
+
+ for (auto&& [tag, val] : cameraMetadata) {
+ switch (tag) {
+ case ANDROID_LENS_DISTORTION:
+ case ANDROID_LENS_POSE_ROTATION:
+ case ANDROID_LENS_POSE_TRANSLATION:
+ case ANDROID_LENS_INTRINSIC_CALIBRATION: {
+ delete[] reinterpret_cast<float*>(val.first);
+ break;
+ }
+
+ case ANDROID_REQUEST_AVAILABLE_CAPABILITIES: {
+ delete[] reinterpret_cast<
+ camera_metadata_enum_android_request_available_capabilities_t*>(val.first);
+ break;
+ }
+
+ case ANDROID_LOGICAL_MULTI_CAMERA_PHYSICAL_IDS: {
+ delete[] reinterpret_cast<char*>(val.first);
+ break;
+ }
+
+ default:
+ LOG(WARNING) << "Tag " << std::hex << tag << " is not supported. "
+ << "Data may be corrupted?";
+ break;
+ }
+ }
+}
diff --git a/automotive/evs/aidl/impl/default/src/ConfigManagerUtil.cpp b/automotive/evs/aidl/impl/default/src/ConfigManagerUtil.cpp
new file mode 100644
index 0000000..e5fe6ef
--- /dev/null
+++ b/automotive/evs/aidl/impl/default/src/ConfigManagerUtil.cpp
@@ -0,0 +1,155 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "ConfigManagerUtil.h"
+
+#include <android-base/logging.h>
+#include <android-base/parsedouble.h>
+#include <android-base/parseint.h>
+#include <linux/videodev2.h>
+
+#include <sstream>
+#include <string>
+
+#include <system/graphics-base-v1.0.h>
+
+using ::aidl::android::hardware::automotive::evs::CameraParam;
+using ::aidl::android::hardware::graphics::common::PixelFormat;
+
+bool ConfigManagerUtil::convertToEvsCameraParam(const std::string& id, CameraParam& camParam) {
+ std::string trimmed = ConfigManagerUtil::trimString(id);
+ bool success = true;
+
+ if (!trimmed.compare("BRIGHTNESS")) {
+ camParam = CameraParam::BRIGHTNESS;
+ } else if (!trimmed.compare("CONTRAST")) {
+ camParam = CameraParam::CONTRAST;
+ } else if (!trimmed.compare("AUTOGAIN")) {
+ camParam = CameraParam::AUTOGAIN;
+ } else if (!trimmed.compare("GAIN")) {
+ camParam = CameraParam::GAIN;
+ } else if (!trimmed.compare("AUTO_WHITE_BALANCE")) {
+ camParam = CameraParam::AUTO_WHITE_BALANCE;
+ } else if (!trimmed.compare("WHITE_BALANCE_TEMPERATURE")) {
+ camParam = CameraParam::WHITE_BALANCE_TEMPERATURE;
+ } else if (!trimmed.compare("SHARPNESS")) {
+ camParam = CameraParam::SHARPNESS;
+ } else if (!trimmed.compare("AUTO_EXPOSURE")) {
+ camParam = CameraParam::AUTO_EXPOSURE;
+ } else if (!trimmed.compare("ABSOLUTE_EXPOSURE")) {
+ camParam = CameraParam::ABSOLUTE_EXPOSURE;
+ } else if (!trimmed.compare("ABSOLUTE_FOCUS")) {
+ camParam = CameraParam::ABSOLUTE_FOCUS;
+ } else if (!trimmed.compare("AUTO_FOCUS")) {
+ camParam = CameraParam::AUTO_FOCUS;
+ } else if (!trimmed.compare("ABSOLUTE_ZOOM")) {
+ camParam = CameraParam::ABSOLUTE_ZOOM;
+ } else {
+ success = false;
+ }
+
+ return success;
+}
+
+bool ConfigManagerUtil::convertToPixelFormat(const std::string& in, PixelFormat& out) {
+ std::string trimmed = ConfigManagerUtil::trimString(in);
+ bool success = true;
+
+ if (!trimmed.compare("RGBA_8888")) {
+ out = PixelFormat::RGBA_8888;
+ } else if (!trimmed.compare("YCRCB_420_SP")) {
+ out = PixelFormat::YCRCB_420_SP;
+ } else if (!trimmed.compare("YCBCR_422_I")) {
+ out = PixelFormat::YCBCR_422_I;
+ } else {
+ out = PixelFormat::UNSPECIFIED;
+ success = false;
+ }
+
+ return success;
+}
+
+bool ConfigManagerUtil::convertToMetadataTag(const char* name, camera_metadata_tag& aTag) {
+ if (!std::strcmp(name, "LENS_DISTORTION")) {
+ aTag = ANDROID_LENS_DISTORTION;
+ } else if (!std::strcmp(name, "LENS_INTRINSIC_CALIBRATION")) {
+ aTag = ANDROID_LENS_INTRINSIC_CALIBRATION;
+ } else if (!std::strcmp(name, "LENS_POSE_ROTATION")) {
+ aTag = ANDROID_LENS_POSE_ROTATION;
+ } else if (!std::strcmp(name, "LENS_POSE_TRANSLATION")) {
+ aTag = ANDROID_LENS_POSE_TRANSLATION;
+ } else if (!std::strcmp(name, "REQUEST_AVAILABLE_CAPABILITIES")) {
+ aTag = ANDROID_REQUEST_AVAILABLE_CAPABILITIES;
+ } else if (!std::strcmp(name, "LOGICAL_MULTI_CAMERA_PHYSICAL_IDS")) {
+ aTag = ANDROID_LOGICAL_MULTI_CAMERA_PHYSICAL_IDS;
+ } else {
+ return false;
+ }
+
+ return true;
+}
+
+bool ConfigManagerUtil::convertToCameraCapability(
+ const char* name, camera_metadata_enum_android_request_available_capabilities_t& cap) {
+ if (!std::strcmp(name, "DEPTH_OUTPUT")) {
+ cap = ANDROID_REQUEST_AVAILABLE_CAPABILITIES_DEPTH_OUTPUT;
+ } else if (!std::strcmp(name, "LOGICAL_MULTI_CAMERA")) {
+ cap = ANDROID_REQUEST_AVAILABLE_CAPABILITIES_LOGICAL_MULTI_CAMERA;
+ } else if (!std::strcmp(name, "MONOCHROME")) {
+ cap = ANDROID_REQUEST_AVAILABLE_CAPABILITIES_MONOCHROME;
+ } else if (!std::strcmp(name, "SECURE_IMAGE_DATA")) {
+ cap = ANDROID_REQUEST_AVAILABLE_CAPABILITIES_SECURE_IMAGE_DATA;
+ } else {
+ return false;
+ }
+
+ return true;
+}
+
+float* ConfigManagerUtil::convertFloatArray(const char* sz, const char* vals, size_t& count,
+ const char delimiter) {
+ std::string size_string(sz);
+ std::string value_string(vals);
+
+ if (!android::base::ParseUint(size_string, &count)) {
+ LOG(ERROR) << "Failed to parse " << size_string;
+ return nullptr;
+ }
+ float* result = new float[count];
+ std::stringstream values(value_string);
+
+ int32_t idx = 0;
+ std::string token;
+ while (getline(values, token, delimiter)) {
+ if (!android::base::ParseFloat(token, &result[idx++])) {
+ LOG(WARNING) << "Failed to parse " << token;
+ }
+ }
+
+ return result;
+}
+
+std::string ConfigManagerUtil::trimString(const std::string& src, const std::string& ws) {
+ const auto s = src.find_first_not_of(ws);
+ if (s == std::string::npos) {
+ return "";
+ }
+
+ const auto e = src.find_last_not_of(ws);
+ const auto r = e - s + 1;
+
+ return src.substr(s, r);
+}
diff --git a/automotive/evs/aidl/impl/default/src/DefaultEvsEnumerator.cpp b/automotive/evs/aidl/impl/default/src/DefaultEvsEnumerator.cpp
deleted file mode 100644
index 5a81d05..0000000
--- a/automotive/evs/aidl/impl/default/src/DefaultEvsEnumerator.cpp
+++ /dev/null
@@ -1,93 +0,0 @@
-/*
- * Copyright (C) 2022 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-// TODO(b/203661081): Remove below lines to disable compiler warnings.
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wunused-parameter"
-
-#define LOG_TAG "DefaultEvsEnumerator"
-
-#include <DefaultEvsEnumerator.h>
-
-namespace aidl::android::hardware::automotive::evs::implementation {
-
-using ::ndk::ScopedAStatus;
-
-ScopedAStatus DefaultEvsEnumerator::isHardware(bool* flag) {
- // This returns true always.
- *flag = true;
- return ScopedAStatus::ok();
-}
-
-ScopedAStatus DefaultEvsEnumerator::openCamera(const std::string& cameraId,
- const Stream& streamConfig,
- std::shared_ptr<IEvsCamera>* obj) {
- return ScopedAStatus::ok();
-}
-
-ScopedAStatus DefaultEvsEnumerator::closeCamera(const std::shared_ptr<IEvsCamera>& obj) {
- return ScopedAStatus::ok();
-}
-
-ScopedAStatus DefaultEvsEnumerator::getCameraList(std::vector<CameraDesc>* list) {
- return ScopedAStatus::ok();
-}
-
-ScopedAStatus DefaultEvsEnumerator::getStreamList(const CameraDesc& desc,
- std::vector<Stream>* _aidl_return) {
- return ScopedAStatus::ok();
-}
-
-ScopedAStatus DefaultEvsEnumerator::openDisplay(int32_t displayId,
- std::shared_ptr<IEvsDisplay>* obj) {
- return ScopedAStatus::ok();
-}
-
-ScopedAStatus DefaultEvsEnumerator::closeDisplay(const std::shared_ptr<IEvsDisplay>& state) {
- return ScopedAStatus::ok();
-}
-
-ScopedAStatus DefaultEvsEnumerator::getDisplayIdList(std::vector<uint8_t>* list) {
- return ScopedAStatus::ok();
-}
-
-ScopedAStatus DefaultEvsEnumerator::getDisplayState(DisplayState* state) {
- return ScopedAStatus::ok();
-}
-
-ScopedAStatus DefaultEvsEnumerator::registerStatusCallback(
- const std::shared_ptr<IEvsEnumeratorStatusCallback>& callback) {
- return ScopedAStatus::ok();
-}
-
-ScopedAStatus DefaultEvsEnumerator::openUltrasonicsArray(
- const std::string& id, std::shared_ptr<IEvsUltrasonicsArray>* obj) {
- return ScopedAStatus::ok();
-}
-
-ScopedAStatus DefaultEvsEnumerator::closeUltrasonicsArray(
- const std::shared_ptr<IEvsUltrasonicsArray>& obj) {
- return ScopedAStatus::ok();
-}
-
-ScopedAStatus DefaultEvsEnumerator::getUltrasonicsArrayList(
- std::vector<UltrasonicsArrayDesc>* list) {
- return ScopedAStatus::ok();
-}
-
-} // namespace aidl::android::hardware::automotive::evs::implementation
-
-#pragma clang diagnostic pop
diff --git a/automotive/evs/aidl/impl/default/src/EvsEnumerator.cpp b/automotive/evs/aidl/impl/default/src/EvsEnumerator.cpp
new file mode 100644
index 0000000..5178958
--- /dev/null
+++ b/automotive/evs/aidl/impl/default/src/EvsEnumerator.cpp
@@ -0,0 +1,560 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "EvsEnumerator.h"
+
+#include "ConfigManager.h"
+#include "EvsGlDisplay.h"
+#include "EvsMockCamera.h"
+
+#include <aidl/android/hardware/automotive/evs/EvsResult.h>
+#include <aidl/android/hardware/graphics/common/BufferUsage.h>
+#include <aidl/android/hardware/graphics/common/PixelFormat.h>
+#include <cutils/android_filesystem_config.h>
+
+#include <set>
+#include <string_view>
+
+namespace {
+
+using ::aidl::android::frameworks::automotive::display::ICarDisplayProxy;
+using ::aidl::android::hardware::graphics::common::BufferUsage;
+using ::ndk::ScopedAStatus;
+using std::chrono_literals::operator""s;
+
+// Constants
+constexpr std::chrono::seconds kEnumerationTimeout = 10s;
+constexpr uint64_t kInvalidDisplayId = std::numeric_limits<uint64_t>::max();
+const std::set<uid_t> kAllowedUids = {AID_AUTOMOTIVE_EVS, AID_SYSTEM, AID_ROOT};
+
+} // namespace
+
+namespace aidl::android::hardware::automotive::evs::implementation {
+
+// NOTE: All members values are static so that all clients operate on the same state
+// That is to say, this is effectively a singleton despite the fact that HIDL
+// constructs a new instance for each client.
+std::unordered_map<std::string, EvsEnumerator::CameraRecord> EvsEnumerator::sCameraList;
+std::mutex EvsEnumerator::sLock;
+std::condition_variable EvsEnumerator::sCameraSignal;
+std::unique_ptr<ConfigManager> EvsEnumerator::sConfigManager;
+std::shared_ptr<ICarDisplayProxy> EvsEnumerator::sDisplayProxy;
+std::unordered_map<uint8_t, uint64_t> EvsEnumerator::sDisplayPortList;
+
+EvsEnumerator::ActiveDisplays& EvsEnumerator::mutableActiveDisplays() {
+ static ActiveDisplays active_displays;
+ return active_displays;
+}
+
+EvsEnumerator::EvsEnumerator(const std::shared_ptr<ICarDisplayProxy>& proxyService) {
+ LOG(DEBUG) << "EvsEnumerator is created.";
+
+ if (!sConfigManager) {
+ /* loads and initializes ConfigManager in a separate thread */
+ sConfigManager = ConfigManager::Create();
+ }
+
+ if (!sDisplayProxy) {
+ /* sets a car-window service handle */
+ sDisplayProxy = proxyService;
+ }
+
+ // Enumerate existing devices
+ enumerateCameras();
+ mInternalDisplayId = enumerateDisplays();
+}
+
+bool EvsEnumerator::checkPermission() {
+ const auto uid = AIBinder_getCallingUid();
+ if (kAllowedUids.find(uid) == kAllowedUids.end()) {
+ LOG(ERROR) << "EVS access denied: "
+ << "pid = " << AIBinder_getCallingPid() << ", uid = " << uid;
+ return false;
+ }
+
+ return true;
+}
+
+void EvsEnumerator::enumerateCameras() {
+ if (!sConfigManager) {
+ return;
+ }
+
+ for (auto id : sConfigManager->getCameraIdList()) {
+ CameraRecord rec(id.data());
+ std::unique_ptr<ConfigManager::CameraInfo>& pInfo = sConfigManager->getCameraInfo(id);
+ if (pInfo) {
+ uint8_t* ptr = reinterpret_cast<uint8_t*>(pInfo->characteristics);
+ const size_t len = get_camera_metadata_size(pInfo->characteristics);
+ rec.desc.metadata.insert(rec.desc.metadata.end(), ptr, ptr + len);
+ }
+ sCameraList.insert_or_assign(id, std::move(rec));
+ }
+}
+
+uint64_t EvsEnumerator::enumerateDisplays() {
+ LOG(INFO) << __FUNCTION__ << ": Starting display enumeration";
+ uint64_t internalDisplayId = kInvalidDisplayId;
+ if (!sDisplayProxy) {
+ LOG(ERROR) << "ICarDisplayProxy is not available!";
+ return internalDisplayId;
+ }
+
+ std::vector<int64_t> displayIds;
+ if (auto status = sDisplayProxy->getDisplayIdList(&displayIds); !status.isOk()) {
+ LOG(ERROR) << "Failed to retrieve a display id list"
+ << ::android::statusToString(status.getStatus());
+ return internalDisplayId;
+ }
+
+ if (displayIds.size() > 0) {
+ // The first entry of the list is the internal display. See
+ // SurfaceFlinger::getPhysicalDisplayIds() implementation.
+ internalDisplayId = displayIds[0];
+ for (const auto& id : displayIds) {
+ const auto port = id & 0xFF;
+ LOG(INFO) << "Display " << std::hex << id << " is detected on the port, " << port;
+ sDisplayPortList.insert_or_assign(port, id);
+ }
+ }
+
+ LOG(INFO) << "Found " << sDisplayPortList.size() << " displays";
+ return internalDisplayId;
+}
+
+// Methods from ::android::hardware::automotive::evs::IEvsEnumerator follow.
+ScopedAStatus EvsEnumerator::getCameraList(std::vector<CameraDesc>* _aidl_return) {
+ LOG(DEBUG) << __FUNCTION__;
+ if (!checkPermission()) {
+ return ScopedAStatus::fromServiceSpecificError(
+ static_cast<int>(EvsResult::PERMISSION_DENIED));
+ }
+
+ {
+ std::unique_lock<std::mutex> lock(sLock);
+ if (sCameraList.size() < 1) {
+ // No qualified device has been found. Wait until new device is ready,
+ // for 10 seconds.
+ if (!sCameraSignal.wait_for(lock, kEnumerationTimeout,
+ [] { return sCameraList.size() > 0; })) {
+ LOG(DEBUG) << "Timer expired. No new device has been added.";
+ }
+ }
+ }
+
+ // Build up a packed array of CameraDesc for return
+ _aidl_return->resize(sCameraList.size());
+ unsigned i = 0;
+ for (const auto& [key, cam] : sCameraList) {
+ (*_aidl_return)[i++] = cam.desc;
+ }
+
+ if (sConfigManager) {
+ // Adding camera groups that represent logical camera devices
+ auto camGroups = sConfigManager->getCameraGroupIdList();
+ for (auto&& id : camGroups) {
+ if (sCameraList.find(id) != sCameraList.end()) {
+ // Already exists in the _aidl_return
+ continue;
+ }
+
+ std::unique_ptr<ConfigManager::CameraGroupInfo>& tempInfo =
+ sConfigManager->getCameraGroupInfo(id);
+ CameraRecord cam(id.data());
+ if (tempInfo) {
+ uint8_t* ptr = reinterpret_cast<uint8_t*>(tempInfo->characteristics);
+ const size_t len = get_camera_metadata_size(tempInfo->characteristics);
+ cam.desc.metadata.insert(cam.desc.metadata.end(), ptr, ptr + len);
+ }
+
+ sCameraList.insert_or_assign(id, cam);
+ _aidl_return->push_back(cam.desc);
+ }
+ }
+
+ // Send back the results
+ LOG(DEBUG) << "Reporting " << sCameraList.size() << " cameras available";
+ return ScopedAStatus::ok();
+}
+
+ScopedAStatus EvsEnumerator::getStreamList(const CameraDesc& desc,
+ std::vector<Stream>* _aidl_return) {
+ using AidlPixelFormat = ::aidl::android::hardware::graphics::common::PixelFormat;
+
+ camera_metadata_t* pMetadata = const_cast<camera_metadata_t*>(
+ reinterpret_cast<const camera_metadata_t*>(desc.metadata.data()));
+ camera_metadata_entry_t streamConfig;
+ if (!find_camera_metadata_entry(pMetadata, ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS,
+ &streamConfig)) {
+ const unsigned numStreamConfigs = streamConfig.count / sizeof(StreamConfiguration);
+ _aidl_return->resize(numStreamConfigs);
+ const StreamConfiguration* pCurrentConfig =
+ reinterpret_cast<StreamConfiguration*>(streamConfig.data.i32);
+ for (unsigned i = 0; i < numStreamConfigs; ++i, ++pCurrentConfig) {
+ // Build ::aidl::android::hardware::automotive::evs::Stream from
+ // StreamConfiguration.
+ Stream current = {
+ .id = pCurrentConfig->id,
+ .streamType =
+ pCurrentConfig->type ==
+ ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS_INPUT
+ ? StreamType::INPUT
+ : StreamType::OUTPUT,
+ .width = pCurrentConfig->width,
+ .height = pCurrentConfig->height,
+ .format = static_cast<AidlPixelFormat>(pCurrentConfig->format),
+ .usage = BufferUsage::CAMERA_INPUT,
+ .rotation = Rotation::ROTATION_0,
+ };
+
+ (*_aidl_return)[i] = current;
+ }
+ }
+
+ return ScopedAStatus::ok();
+}
+
+ScopedAStatus EvsEnumerator::openCamera(const std::string& id, const Stream& cfg,
+ std::shared_ptr<IEvsCamera>* obj) {
+ LOG(DEBUG) << __FUNCTION__;
+ if (!checkPermission()) {
+ return ScopedAStatus::fromServiceSpecificError(
+ static_cast<int>(EvsResult::PERMISSION_DENIED));
+ }
+
+ // Is this a recognized camera id?
+ CameraRecord* pRecord = findCameraById(id);
+ if (!pRecord) {
+ LOG(ERROR) << id << " does not exist!";
+ return ScopedAStatus::fromServiceSpecificError(static_cast<int>(EvsResult::INVALID_ARG));
+ }
+
+ // Has this camera already been instantiated by another caller?
+ std::shared_ptr<EvsMockCamera> pActiveCamera = pRecord->activeInstance.lock();
+ if (pActiveCamera) {
+ LOG(WARNING) << "Killing previous camera because of new caller";
+ closeCamera(pActiveCamera);
+ }
+
+ // Construct a camera instance for the caller
+ if (!sConfigManager) {
+ pActiveCamera = EvsMockCamera::Create(id.data());
+ } else {
+ pActiveCamera = EvsMockCamera::Create(id.data(), sConfigManager->getCameraInfo(id), &cfg);
+ }
+
+ pRecord->activeInstance = pActiveCamera;
+ if (!pActiveCamera) {
+ LOG(ERROR) << "Failed to create new EvsMockCamera object for " << id;
+ return ScopedAStatus::fromServiceSpecificError(
+ static_cast<int>(EvsResult::UNDERLYING_SERVICE_ERROR));
+ }
+
+ *obj = pActiveCamera;
+ return ScopedAStatus::ok();
+}
+
+ScopedAStatus EvsEnumerator::closeCamera(const std::shared_ptr<IEvsCamera>& cameraObj) {
+ LOG(DEBUG) << __FUNCTION__;
+
+ if (!cameraObj) {
+ LOG(ERROR) << "Ignoring call to closeCamera with null camera ptr";
+ return ScopedAStatus::fromServiceSpecificError(static_cast<int>(EvsResult::INVALID_ARG));
+ }
+
+ // Get the camera id so we can find it in our list
+ CameraDesc desc;
+ auto status = cameraObj->getCameraInfo(&desc);
+ if (!status.isOk()) {
+ LOG(ERROR) << "Failed to read a camera descriptor";
+ return ScopedAStatus::fromServiceSpecificError(
+ static_cast<int>(EvsResult::UNDERLYING_SERVICE_ERROR));
+ }
+ auto cameraId = desc.id;
+ closeCamera_impl(cameraObj, cameraId);
+ return ScopedAStatus::ok();
+}
+
+ScopedAStatus EvsEnumerator::openDisplay(int32_t id, std::shared_ptr<IEvsDisplay>* displayObj) {
+ LOG(DEBUG) << __FUNCTION__;
+ if (!checkPermission()) {
+ return ScopedAStatus::fromServiceSpecificError(
+ static_cast<int>(EvsResult::PERMISSION_DENIED));
+ }
+
+ auto& displays = mutableActiveDisplays();
+
+ if (auto existing_display_search = displays.popDisplay(id)) {
+ // If we already have a display active, then we need to shut it down so we can
+ // give exclusive access to the new caller.
+ std::shared_ptr<EvsGlDisplay> pActiveDisplay = existing_display_search->displayWeak.lock();
+ if (pActiveDisplay) {
+ LOG(WARNING) << "Killing previous display because of new caller";
+ pActiveDisplay->forceShutdown();
+ }
+ }
+
+ // Create a new display interface and return it
+ uint64_t targetDisplayId = mInternalDisplayId;
+ auto it = sDisplayPortList.find(id);
+ if (it != sDisplayPortList.end()) {
+ targetDisplayId = it->second;
+ } else {
+ LOG(WARNING) << "No display is available on the port " << static_cast<int32_t>(id)
+ << ". The main display " << mInternalDisplayId << " will be used instead";
+ }
+
+ // Create a new display interface and return it.
+ std::shared_ptr<EvsGlDisplay> pActiveDisplay =
+ ndk::SharedRefBase::make<EvsGlDisplay>(sDisplayProxy, targetDisplayId);
+
+ if (auto insert_result = displays.tryInsert(id, pActiveDisplay); !insert_result) {
+ LOG(ERROR) << "Display ID " << id << " has been used by another caller.";
+ pActiveDisplay->forceShutdown();
+ return ScopedAStatus::fromServiceSpecificError(static_cast<int>(EvsResult::RESOURCE_BUSY));
+ }
+
+ LOG(DEBUG) << "Returning new EvsGlDisplay object " << pActiveDisplay.get();
+ *displayObj = pActiveDisplay;
+ return ScopedAStatus::ok();
+}
+
+ScopedAStatus EvsEnumerator::closeDisplay(const std::shared_ptr<IEvsDisplay>& obj) {
+ LOG(DEBUG) << __FUNCTION__;
+
+ auto& displays = mutableActiveDisplays();
+ const auto display_search = displays.popDisplay(obj);
+
+ if (!display_search) {
+ LOG(WARNING) << "Ignoring close of previously orphaned display - why did a client steal?";
+ return ScopedAStatus::ok();
+ }
+
+ auto pActiveDisplay = display_search->displayWeak.lock();
+
+ if (!pActiveDisplay) {
+ LOG(ERROR) << "Somehow a display is being destroyed "
+ << "when the enumerator didn't know one existed";
+ return ScopedAStatus::fromServiceSpecificError(static_cast<int>(EvsResult::OWNERSHIP_LOST));
+ }
+
+ pActiveDisplay->forceShutdown();
+ return ScopedAStatus::ok();
+}
+
+ScopedAStatus EvsEnumerator::getDisplayState(DisplayState* state) {
+ LOG(DEBUG) << __FUNCTION__;
+ return getDisplayStateImpl(std::nullopt, state);
+}
+
+ScopedAStatus EvsEnumerator::getDisplayStateById(int32_t displayId, DisplayState* state) {
+ LOG(DEBUG) << __FUNCTION__;
+ return getDisplayStateImpl(displayId, state);
+}
+
+ScopedAStatus EvsEnumerator::getDisplayStateImpl(std::optional<int32_t> displayId,
+ DisplayState* state) {
+ if (!checkPermission()) {
+ *state = DisplayState::DEAD;
+ return ScopedAStatus::fromServiceSpecificError(
+ static_cast<int>(EvsResult::PERMISSION_DENIED));
+ }
+
+ const auto& all_displays = mutableActiveDisplays().getAllDisplays();
+
+ const auto display_search = displayId ? all_displays.find(*displayId) : all_displays.begin();
+
+ if (display_search == all_displays.end()) {
+ *state = DisplayState::NOT_OPEN;
+ return ScopedAStatus::fromServiceSpecificError(static_cast<int>(EvsResult::OWNERSHIP_LOST));
+ }
+
+ std::shared_ptr<IEvsDisplay> pActiveDisplay = display_search->second.displayWeak.lock();
+ if (pActiveDisplay) {
+ return pActiveDisplay->getDisplayState(state);
+ } else {
+ *state = DisplayState::NOT_OPEN;
+ return ScopedAStatus::fromServiceSpecificError(static_cast<int>(EvsResult::OWNERSHIP_LOST));
+ }
+}
+
+ScopedAStatus EvsEnumerator::getDisplayIdList(std::vector<uint8_t>* list) {
+ std::vector<uint8_t>& output = *list;
+ if (sDisplayPortList.size() > 0) {
+ output.resize(sDisplayPortList.size());
+ unsigned i = 0;
+ output[i++] = mInternalDisplayId & 0xFF;
+ for (const auto& [port, id] : sDisplayPortList) {
+ if (mInternalDisplayId != id) {
+ output[i++] = port;
+ }
+ }
+ }
+
+ return ScopedAStatus::ok();
+}
+
+ScopedAStatus EvsEnumerator::isHardware(bool* flag) {
+ *flag = true;
+ return ScopedAStatus::ok();
+}
+
+void EvsEnumerator::notifyDeviceStatusChange(const std::string_view& deviceName,
+ DeviceStatusType type) {
+ std::lock_guard lock(sLock);
+ if (!mCallback) {
+ return;
+ }
+
+ std::vector<DeviceStatus> status{{.id = std::string(deviceName), .status = type}};
+ if (!mCallback->deviceStatusChanged(status).isOk()) {
+ LOG(WARNING) << "Failed to notify a device status change, name = " << deviceName
+ << ", type = " << static_cast<int>(type);
+ }
+}
+
+ScopedAStatus EvsEnumerator::registerStatusCallback(
+ const std::shared_ptr<IEvsEnumeratorStatusCallback>& callback) {
+ std::lock_guard lock(sLock);
+ if (mCallback) {
+ LOG(INFO) << "Replacing an existing device status callback";
+ }
+ mCallback = callback;
+ return ScopedAStatus::ok();
+}
+
+void EvsEnumerator::closeCamera_impl(const std::shared_ptr<IEvsCamera>& pCamera,
+ const std::string& cameraId) {
+ // Find the named camera
+ CameraRecord* pRecord = findCameraById(cameraId);
+
+ // Is the display being destroyed actually the one we think is active?
+ if (!pRecord) {
+ LOG(ERROR) << "Asked to close a camera whose name isn't recognized";
+ } else {
+ std::shared_ptr<EvsMockCamera> pActiveCamera = pRecord->activeInstance.lock();
+ if (!pActiveCamera) {
+ LOG(WARNING) << "Somehow a camera is being destroyed "
+ << "when the enumerator didn't know one existed";
+ } else if (pActiveCamera != pCamera) {
+ // This can happen if the camera was aggressively reopened,
+ // orphaning this previous instance
+ LOG(WARNING) << "Ignoring close of previously orphaned camera "
+ << "- why did a client steal?";
+ } else {
+ // Shutdown the active camera
+ pActiveCamera->shutdown();
+ }
+ }
+
+ return;
+}
+
+EvsEnumerator::CameraRecord* EvsEnumerator::findCameraById(const std::string& cameraId) {
+ // Find the named camera
+ auto found = sCameraList.find(cameraId);
+ if (found != sCameraList.end()) {
+ // Found a match!
+ return &found->second;
+ }
+
+ // We didn't find a match
+ return nullptr;
+}
+
+std::optional<EvsEnumerator::ActiveDisplays::DisplayInfo> EvsEnumerator::ActiveDisplays::popDisplay(
+ int32_t id) {
+ std::lock_guard lck(mMutex);
+ const auto search = mIdToDisplay.find(id);
+ if (search == mIdToDisplay.end()) {
+ return std::nullopt;
+ }
+ const auto display_info = search->second;
+ mIdToDisplay.erase(search);
+ mDisplayToId.erase(display_info.internalDisplayRawAddr);
+ return display_info;
+}
+
+std::optional<EvsEnumerator::ActiveDisplays::DisplayInfo> EvsEnumerator::ActiveDisplays::popDisplay(
+ const std::shared_ptr<IEvsDisplay>& display) {
+ const auto display_ptr_val = reinterpret_cast<uintptr_t>(display.get());
+ std::lock_guard lck(mMutex);
+ const auto display_to_id_search = mDisplayToId.find(display_ptr_val);
+ if (display_to_id_search == mDisplayToId.end()) {
+ LOG(ERROR) << "Unknown display.";
+ return std::nullopt;
+ }
+ const auto id = display_to_id_search->second;
+ const auto id_to_display_search = mIdToDisplay.find(id);
+ mDisplayToId.erase(display_to_id_search);
+ if (id_to_display_search == mIdToDisplay.end()) {
+ LOG(ERROR) << "No correspsonding ID for the display, probably orphaned.";
+ return std::nullopt;
+ }
+ const auto display_info = id_to_display_search->second;
+ mIdToDisplay.erase(id);
+ return display_info;
+}
+
+std::unordered_map<int32_t, EvsEnumerator::ActiveDisplays::DisplayInfo>
+EvsEnumerator::ActiveDisplays::getAllDisplays() {
+ std::lock_guard lck(mMutex);
+ auto id_to_display_map_copy = mIdToDisplay;
+ return id_to_display_map_copy;
+}
+
+bool EvsEnumerator::ActiveDisplays::tryInsert(int32_t id,
+ const std::shared_ptr<EvsGlDisplay>& display) {
+ std::lock_guard lck(mMutex);
+ const auto display_ptr_val = reinterpret_cast<uintptr_t>(display.get());
+
+ auto id_to_display_insert_result =
+ mIdToDisplay.emplace(id, DisplayInfo{
+ .id = id,
+ .displayWeak = display,
+ .internalDisplayRawAddr = display_ptr_val,
+ });
+ if (!id_to_display_insert_result.second) {
+ return false;
+ }
+ auto display_to_id_insert_result = mDisplayToId.emplace(display_ptr_val, id);
+ if (!display_to_id_insert_result.second) {
+ mIdToDisplay.erase(id);
+ return false;
+ }
+ return true;
+}
+
+ScopedAStatus EvsEnumerator::getUltrasonicsArrayList(
+ [[maybe_unused]] std::vector<UltrasonicsArrayDesc>* list) {
+ // TODO(b/149874793): Add implementation for EVS Manager and Sample driver
+ return ScopedAStatus::ok();
+}
+
+ScopedAStatus EvsEnumerator::openUltrasonicsArray(
+ [[maybe_unused]] const std::string& id,
+ [[maybe_unused]] std::shared_ptr<IEvsUltrasonicsArray>* obj) {
+ // TODO(b/149874793): Add implementation for EVS Manager and Sample driver
+ return ScopedAStatus::ok();
+}
+
+ScopedAStatus EvsEnumerator::closeUltrasonicsArray(
+ [[maybe_unused]] const std::shared_ptr<IEvsUltrasonicsArray>& obj) {
+ // TODO(b/149874793): Add implementation for EVS Manager and Sample driver
+ return ScopedAStatus::ok();
+}
+
+} // namespace aidl::android::hardware::automotive::evs::implementation
diff --git a/automotive/evs/aidl/impl/default/src/EvsGlDisplay.cpp b/automotive/evs/aidl/impl/default/src/EvsGlDisplay.cpp
new file mode 100644
index 0000000..e5f8e4c
--- /dev/null
+++ b/automotive/evs/aidl/impl/default/src/EvsGlDisplay.cpp
@@ -0,0 +1,417 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "EvsGlDisplay.h"
+
+#include <aidl/android/hardware/automotive/evs/EvsResult.h>
+#include <aidl/android/hardware/graphics/common/BufferUsage.h>
+#include <aidl/android/hardware/graphics/common/PixelFormat.h>
+#include <aidlcommonsupport/NativeHandle.h>
+#include <android-base/thread_annotations.h>
+#include <linux/time.h>
+#include <ui/DisplayMode.h>
+#include <ui/DisplayState.h>
+#include <ui/GraphicBufferAllocator.h>
+#include <ui/GraphicBufferMapper.h>
+#include <utils/SystemClock.h>
+
+#include <chrono>
+
+namespace {
+
+using ::aidl::android::frameworks::automotive::display::ICarDisplayProxy;
+using ::aidl::android::hardware::graphics::common::BufferUsage;
+using ::aidl::android::hardware::graphics::common::PixelFormat;
+using ::android::base::ScopedLockAssertion;
+using ::ndk::ScopedAStatus;
+
+constexpr auto kTimeout = std::chrono::seconds(1);
+
+bool debugFirstFrameDisplayed = false;
+
+int generateFingerPrint(buffer_handle_t handle) {
+ return static_cast<int>(reinterpret_cast<long>(handle) & 0xFFFFFFFF);
+}
+
+} // namespace
+
+namespace aidl::android::hardware::automotive::evs::implementation {
+
+EvsGlDisplay::EvsGlDisplay(const std::shared_ptr<ICarDisplayProxy>& pDisplayProxy,
+ uint64_t displayId)
+ : mDisplayId(displayId), mDisplayProxy(pDisplayProxy) {
+ LOG(DEBUG) << "EvsGlDisplay instantiated";
+
+ // Set up our self description
+ // NOTE: These are arbitrary values chosen for testing
+ mInfo.id = std::to_string(displayId);
+ mInfo.vendorFlags = 3870;
+
+ // Start a thread to render images on this display
+ {
+ std::lock_guard lock(mLock);
+ mState = RUN;
+ }
+ mRenderThread = std::thread([this]() { renderFrames(); });
+}
+
+EvsGlDisplay::~EvsGlDisplay() {
+ LOG(DEBUG) << "EvsGlDisplay being destroyed";
+ forceShutdown();
+}
+
+/**
+ * This gets called if another caller "steals" ownership of the display
+ */
+void EvsGlDisplay::forceShutdown() {
+ LOG(DEBUG) << "EvsGlDisplay forceShutdown";
+ {
+ std::lock_guard lock(mLock);
+
+ // If the buffer isn't being held by a remote client, release it now as an
+ // optimization to release the resources more quickly than the destructor might
+ // get called.
+ if (mBuffer.handle != nullptr) {
+ // Report if we're going away while a buffer is outstanding
+ if (mBufferBusy || mState == RUN) {
+ LOG(ERROR) << "EvsGlDisplay going down while client is holding a buffer";
+ }
+ mState = STOPPING;
+ }
+
+ // Put this object into an unrecoverable error state since somebody else
+ // is going to own the display now.
+ mRequestedState = DisplayState::DEAD;
+ }
+ mBufferReadyToRender.notify_all();
+
+ if (mRenderThread.joinable()) {
+ mRenderThread.join();
+ }
+}
+
+/**
+ * Initialize GL in the context of a caller's thread and prepare a graphic
+ * buffer to use.
+ */
+bool EvsGlDisplay::initializeGlContextLocked() {
+ // Initialize our display window
+ // NOTE: This will cause the display to become "VISIBLE" before a frame is actually
+ // returned, which is contrary to the spec and will likely result in a black frame being
+ // (briefly) shown.
+ if (!mGlWrapper.initialize(mDisplayProxy, mDisplayId)) {
+ // Report the failure
+ LOG(ERROR) << "Failed to initialize GL display";
+ return false;
+ }
+
+ // Assemble the buffer description we'll use for our render target
+ static_assert(::aidl::android::hardware::graphics::common::PixelFormat::RGBA_8888 ==
+ static_cast<::aidl::android::hardware::graphics::common::PixelFormat>(
+ HAL_PIXEL_FORMAT_RGBA_8888));
+ mBuffer.description = {
+ .width = static_cast<int>(mGlWrapper.getWidth()),
+ .height = static_cast<int>(mGlWrapper.getHeight()),
+ .layers = 1,
+ .format = PixelFormat::RGBA_8888,
+ // FIXME: Below line is not using
+ // ::aidl::android::hardware::graphics::common::BufferUsage because
+ // BufferUsage enum does not support a bitwise-OR operation; they
+ // should be BufferUsage::GPU_RENDER_TARGET |
+ // BufferUsage::COMPOSER_OVERLAY
+ .usage = static_cast<BufferUsage>(GRALLOC_USAGE_HW_RENDER | GRALLOC_USAGE_HW_COMPOSER),
+ };
+
+ ::android::GraphicBufferAllocator& alloc(::android::GraphicBufferAllocator::get());
+ uint32_t stride = static_cast<uint32_t>(mBuffer.description.stride);
+ buffer_handle_t handle = nullptr;
+ const ::android::status_t result =
+ alloc.allocate(mBuffer.description.width, mBuffer.description.height,
+ static_cast<::android::PixelFormat>(mBuffer.description.format),
+ mBuffer.description.layers,
+ static_cast<uint64_t>(mBuffer.description.usage), &handle, &stride,
+ /* requestorName= */ "EvsGlDisplay");
+ mBuffer.description.stride = stride;
+ mBuffer.fingerprint = generateFingerPrint(mBuffer.handle);
+ if (result != ::android::NO_ERROR) {
+ LOG(ERROR) << "Error " << result << " allocating " << mBuffer.description.width << " x "
+ << mBuffer.description.height << " graphics buffer.";
+ mGlWrapper.shutdown();
+ return false;
+ }
+
+ mBuffer.handle = handle;
+ if (mBuffer.handle == nullptr) {
+ LOG(ERROR) << "We didn't get a buffer handle back from the allocator";
+ mGlWrapper.shutdown();
+ return false;
+ }
+
+ LOG(DEBUG) << "Allocated new buffer " << mBuffer.handle << " with stride "
+ << mBuffer.description.stride;
+ return true;
+}
+
+/**
+ * This method runs in a separate thread and renders the contents of the buffer.
+ */
+void EvsGlDisplay::renderFrames() {
+ {
+ std::lock_guard lock(mLock);
+
+ if (!initializeGlContextLocked()) {
+ LOG(ERROR) << "Failed to initialize GL context";
+ return;
+ }
+
+ // Display buffer is ready.
+ mBufferBusy = false;
+ }
+ mBufferReadyToUse.notify_all();
+
+ while (true) {
+ {
+ std::unique_lock lock(mLock);
+ ScopedLockAssertion lock_assertion(mLock);
+ mBufferReadyToRender.wait(
+ lock, [this]() REQUIRES(mLock) { return mBufferReady || mState != RUN; });
+ if (mState != RUN) {
+ LOG(DEBUG) << "A rendering thread is stopping";
+ break;
+ }
+ mBufferReady = false;
+ }
+
+ // Update the texture contents with the provided data
+ if (!mGlWrapper.updateImageTexture(mBuffer.handle, mBuffer.description)) {
+ LOG(WARNING) << "Failed to update the image texture";
+ continue;
+ }
+
+ // Put the image on the screen
+ mGlWrapper.renderImageToScreen();
+ if (!debugFirstFrameDisplayed) {
+ LOG(DEBUG) << "EvsFirstFrameDisplayTiming start time: " << ::android::elapsedRealtime()
+ << " ms.";
+ debugFirstFrameDisplayed = true;
+ }
+
+ // Mark current frame is consumed.
+ {
+ std::lock_guard lock(mLock);
+ mBufferBusy = false;
+ }
+ mBufferDone.notify_all();
+ }
+
+ LOG(DEBUG) << "A rendering thread is stopped.";
+
+ // Drop the graphics buffer we've been using
+ ::android::GraphicBufferAllocator& alloc(::android::GraphicBufferAllocator::get());
+ alloc.free(mBuffer.handle);
+ mBuffer.handle = nullptr;
+
+ mGlWrapper.hideWindow(mDisplayProxy, mDisplayId);
+ mGlWrapper.shutdown();
+
+ std::lock_guard lock(mLock);
+ mState = STOPPED;
+}
+
+/**
+ * Returns basic information about the EVS display provided by the system.
+ * See the description of the DisplayDesc structure for details.
+ */
+ScopedAStatus EvsGlDisplay::getDisplayInfo(DisplayDesc* _aidl_return) {
+ if (!mDisplayProxy) {
+ return ::ndk::ScopedAStatus::fromServiceSpecificError(
+ static_cast<int>(EvsResult::UNDERLYING_SERVICE_ERROR));
+ }
+
+ ::aidl::android::frameworks::automotive::display::DisplayDesc proxyDisplay;
+ auto status = mDisplayProxy->getDisplayInfo(mDisplayId, &proxyDisplay);
+ if (!status.isOk()) {
+ return ::ndk::ScopedAStatus::fromServiceSpecificError(
+ static_cast<int>(EvsResult::UNDERLYING_SERVICE_ERROR));
+ }
+
+ _aidl_return->width = proxyDisplay.width;
+ _aidl_return->height = proxyDisplay.height;
+ _aidl_return->orientation = static_cast<Rotation>(proxyDisplay.orientation);
+ _aidl_return->id = mInfo.id; // FIXME: what should be ID here?
+ _aidl_return->vendorFlags = mInfo.vendorFlags;
+ return ::ndk::ScopedAStatus::ok();
+}
+
+/**
+ * Clients may set the display state to express their desired state.
+ * The HAL implementation must gracefully accept a request for any state
+ * while in any other state, although the response may be to ignore the request.
+ * The display is defined to start in the NOT_VISIBLE state upon initialization.
+ * The client is then expected to request the VISIBLE_ON_NEXT_FRAME state, and
+ * then begin providing video. When the display is no longer required, the client
+ * is expected to request the NOT_VISIBLE state after passing the last video frame.
+ */
+ScopedAStatus EvsGlDisplay::setDisplayState(DisplayState state) {
+ LOG(DEBUG) << __FUNCTION__;
+ std::lock_guard lock(mLock);
+
+ if (mRequestedState == DisplayState::DEAD) {
+ // This object no longer owns the display -- it's been superceeded!
+ return ScopedAStatus::fromServiceSpecificError(static_cast<int>(EvsResult::OWNERSHIP_LOST));
+ }
+
+ // Ensure we recognize the requested state so we don't go off the rails
+ static constexpr ::ndk::enum_range<DisplayState> kDisplayStateRange;
+ if (std::find(kDisplayStateRange.begin(), kDisplayStateRange.end(), state) ==
+ kDisplayStateRange.end()) {
+ return ScopedAStatus::fromServiceSpecificError(static_cast<int>(EvsResult::INVALID_ARG));
+ }
+
+ switch (state) {
+ case DisplayState::NOT_VISIBLE:
+ mGlWrapper.hideWindow(mDisplayProxy, mDisplayId);
+ break;
+ case DisplayState::VISIBLE:
+ mGlWrapper.showWindow(mDisplayProxy, mDisplayId);
+ break;
+ default:
+ break;
+ }
+
+ // Record the requested state
+ mRequestedState = state;
+
+ return ScopedAStatus::ok();
+}
+
+/**
+ * The HAL implementation should report the actual current state, which might
+ * transiently differ from the most recently requested state. Note, however, that
+ * the logic responsible for changing display states should generally live above
+ * the device layer, making it undesirable for the HAL implementation to
+ * spontaneously change display states.
+ */
+ScopedAStatus EvsGlDisplay::getDisplayState(DisplayState* _aidl_return) {
+ LOG(DEBUG) << __FUNCTION__;
+ std::lock_guard lock(mLock);
+ *_aidl_return = mRequestedState;
+ return ScopedAStatus::ok();
+}
+
+/**
+ * This call returns a handle to a frame buffer associated with the display.
+ * This buffer may be locked and written to by software and/or GL. This buffer
+ * must be returned via a call to returnTargetBufferForDisplay() even if the
+ * display is no longer visible.
+ */
+ScopedAStatus EvsGlDisplay::getTargetBuffer(BufferDesc* _aidl_return) {
+ LOG(DEBUG) << __FUNCTION__;
+ std::unique_lock lock(mLock);
+ ScopedLockAssertion lock_assertion(mLock);
+ if (mRequestedState == DisplayState::DEAD) {
+ LOG(ERROR) << "Rejecting buffer request from object that lost ownership of the display.";
+ return ScopedAStatus::fromServiceSpecificError(static_cast<int>(EvsResult::OWNERSHIP_LOST));
+ }
+
+ // If we don't already have a buffer, allocate one now
+ // mBuffer.memHandle is a type of buffer_handle_t, which is equal to
+ // native_handle_t*.
+ mBufferReadyToUse.wait(lock, [this]() REQUIRES(mLock) { return !mBufferBusy; });
+
+ // Do we have a frame available?
+ if (mBufferBusy) {
+ // This means either we have a 2nd client trying to compete for buffers
+ // (an unsupported mode of operation) or else the client hasn't returned
+ // a previously issued buffer yet (they're behaving badly).
+ // NOTE: We have to make the callback even if we have nothing to provide
+ LOG(ERROR) << "getTargetBuffer called while no buffers available.";
+ return ScopedAStatus::fromServiceSpecificError(
+ static_cast<int>(EvsResult::BUFFER_NOT_AVAILABLE));
+ }
+
+ // Mark our buffer as busy
+ mBufferBusy = true;
+
+ // Send the buffer to the client
+ LOG(VERBOSE) << "Providing display buffer handle " << mBuffer.handle;
+
+ BufferDesc bufferDescToSend = {
+ .buffer =
+ {
+ .handle = std::move(::android::dupToAidl(mBuffer.handle)),
+ .description = mBuffer.description,
+ },
+ .pixelSizeBytes = 4, // RGBA_8888 is 4-byte-per-pixel format
+ .bufferId = mBuffer.fingerprint,
+ };
+ *_aidl_return = std::move(bufferDescToSend);
+
+ return ScopedAStatus::ok();
+}
+
+/**
+ * This call tells the display that the buffer is ready for display.
+ * The buffer is no longer valid for use by the client after this call.
+ */
+ScopedAStatus EvsGlDisplay::returnTargetBufferForDisplay(const BufferDesc& buffer) {
+ LOG(VERBOSE) << __FUNCTION__;
+ std::unique_lock lock(mLock);
+ ScopedLockAssertion lock_assertion(mLock);
+
+ // Nobody should call us with a null handle
+ if (buffer.buffer.handle.fds.size() < 1) {
+ LOG(ERROR) << __FUNCTION__ << " called without a valid buffer handle.";
+ return ScopedAStatus::fromServiceSpecificError(static_cast<int>(EvsResult::INVALID_ARG));
+ }
+ if (buffer.bufferId != mBuffer.fingerprint) {
+ LOG(ERROR) << "Got an unrecognized frame returned.";
+ return ScopedAStatus::fromServiceSpecificError(static_cast<int>(EvsResult::INVALID_ARG));
+ }
+ if (!mBufferBusy) {
+ LOG(ERROR) << "A frame was returned with no outstanding frames.";
+ return ScopedAStatus::fromServiceSpecificError(static_cast<int>(EvsResult::INVALID_ARG));
+ }
+
+ // If we've been displaced by another owner of the display, then we can't do anything else
+ if (mRequestedState == DisplayState::DEAD) {
+ return ScopedAStatus::fromServiceSpecificError(static_cast<int>(EvsResult::OWNERSHIP_LOST));
+ }
+
+ // If we were waiting for a new frame, this is it!
+ if (mRequestedState == DisplayState::VISIBLE_ON_NEXT_FRAME) {
+ mRequestedState = DisplayState::VISIBLE;
+ mGlWrapper.showWindow(mDisplayProxy, mDisplayId);
+ }
+
+ // Validate we're in an expected state
+ if (mRequestedState != DisplayState::VISIBLE) {
+ // Not sure why a client would send frames back when we're not visible.
+ LOG(WARNING) << "Got a frame returned while not visible - ignoring.";
+ return ScopedAStatus::ok();
+ }
+ mBufferReady = true;
+ mBufferReadyToRender.notify_all();
+
+ if (!mBufferDone.wait_for(lock, kTimeout, [this]() REQUIRES(mLock) { return !mBufferBusy; })) {
+ return ScopedAStatus::fromServiceSpecificError(
+ static_cast<int>(EvsResult::UNDERLYING_SERVICE_ERROR));
+ }
+
+ return ScopedAStatus::ok();
+}
+
+} // namespace aidl::android::hardware::automotive::evs::implementation
diff --git a/automotive/evs/aidl/impl/default/src/EvsMockCamera.cpp b/automotive/evs/aidl/impl/default/src/EvsMockCamera.cpp
new file mode 100644
index 0000000..797b221
--- /dev/null
+++ b/automotive/evs/aidl/impl/default/src/EvsMockCamera.cpp
@@ -0,0 +1,735 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "EvsMockCamera.h"
+#include "ConfigManager.h"
+#include "EvsEnumerator.h"
+
+#include <aidlcommonsupport/NativeHandle.h>
+#include <ui/GraphicBufferAllocator.h>
+#include <ui/GraphicBufferMapper.h>
+#include <utils/SystemClock.h>
+
+#include <memory>
+
+namespace {
+
+using ::aidl::android::hardware::graphics::common::BufferUsage;
+using ::ndk::ScopedAStatus;
+
+// Arbitrary limit on number of graphics buffers allowed to be allocated
+// Safeguards against unreasonable resource consumption and provides a testable limit
+constexpr unsigned kMaxBuffersInFlight = 100;
+
+// Minimum number of buffers to run a video stream
+constexpr int kMinimumBuffersInFlight = 1;
+
+// Colors for the colorbar test pattern in ABGR format
+constexpr uint32_t kColors[] = {
+ 0xFFFFFFFF, // white
+ 0xFF00FFFF, // yellow
+ 0xFFFFFF00, // cyan
+ 0xFF00FF00, // green
+ 0xFFFF00FF, // fuchsia
+ 0xFF0000FF, // red
+ 0xFFFF0000, // blue
+ 0xFF000000, // black
+};
+constexpr size_t kNumColors = sizeof(kColors) / sizeof(kColors[0]);
+
+} // namespace
+
+namespace aidl::android::hardware::automotive::evs::implementation {
+
+EvsMockCamera::EvsMockCamera([[maybe_unused]] Sigil sigil, const char* id,
+ std::unique_ptr<ConfigManager::CameraInfo>& camInfo)
+ : mFramesAllowed(0), mFramesInUse(0), mStreamState(STOPPED), mCameraInfo(camInfo) {
+ LOG(DEBUG) << __FUNCTION__;
+
+ /* set a camera id */
+ mDescription.id = id;
+
+ /* set camera metadata */
+ if (camInfo) {
+ uint8_t* ptr = reinterpret_cast<uint8_t*>(camInfo->characteristics);
+ const size_t len = get_camera_metadata_size(camInfo->characteristics);
+ mDescription.metadata.insert(mDescription.metadata.end(), ptr, ptr + len);
+ }
+
+ // Initialize parameters.
+ initializeParameters();
+}
+
+EvsMockCamera::~EvsMockCamera() {
+ LOG(DEBUG) << __FUNCTION__;
+ shutdown();
+}
+
+void EvsMockCamera::initializeParameters() {
+ mParams.emplace(
+ CameraParam::BRIGHTNESS,
+ new CameraParameterDesc(/* min= */ 0, /* max= */ 255, /* step= */ 1, /* value= */ 255));
+ mParams.emplace(
+ CameraParam::CONTRAST,
+ new CameraParameterDesc(/* min= */ 0, /* max= */ 255, /* step= */ 1, /* value= */ 255));
+ mParams.emplace(
+ CameraParam::SHARPNESS,
+ new CameraParameterDesc(/* min= */ 0, /* max= */ 255, /* step= */ 1, /* value= */ 255));
+}
+
+// This gets called if another caller "steals" ownership of the camera
+void EvsMockCamera::shutdown() {
+ LOG(DEBUG) << __FUNCTION__;
+
+ // Make sure our output stream is cleaned up
+ // (It really should be already)
+ stopVideoStream_impl();
+
+ // Claim the lock while we work on internal state
+ std::lock_guard lock(mAccessLock);
+
+ // Drop all the graphics buffers we've been using
+ if (mBuffers.size() > 0) {
+ ::android::GraphicBufferAllocator& alloc(::android::GraphicBufferAllocator::get());
+ for (auto&& rec : mBuffers) {
+ if (rec.inUse) {
+ LOG(WARNING) << "WARNING: releasing a buffer remotely owned.";
+ }
+ alloc.free(rec.handle);
+ rec.handle = nullptr;
+ }
+ mBuffers.clear();
+ }
+
+ // Put this object into an unrecoverable error state since somebody else
+ // is going to own the underlying camera now
+ mStreamState = DEAD;
+}
+
+// Methods from ::aidl::android::hardware::automotive::evs::IEvsCamera follow.
+ScopedAStatus EvsMockCamera::getCameraInfo(CameraDesc* _aidl_return) {
+ LOG(DEBUG) << __FUNCTION__;
+
+ // Send back our self description
+ *_aidl_return = mDescription;
+ return ScopedAStatus::ok();
+}
+
+ScopedAStatus EvsMockCamera::setMaxFramesInFlight(int32_t bufferCount) {
+ LOG(DEBUG) << __FUNCTION__ << ", bufferCount = " << bufferCount;
+ ;
+
+ std::lock_guard lock(mAccessLock);
+
+ // If we've been displaced by another owner of the camera, then we can't do anything else
+ if (mStreamState == DEAD) {
+ LOG(ERROR) << "Ignoring setMaxFramesInFlight call when camera has been lost.";
+ return ScopedAStatus::fromServiceSpecificError(static_cast<int>(EvsResult::OWNERSHIP_LOST));
+ }
+
+ // We cannot function without at least one video buffer to send data
+ if (bufferCount < 1) {
+ LOG(ERROR) << "Ignoring setMaxFramesInFlight with less than one buffer requested.";
+ return ScopedAStatus::fromServiceSpecificError(static_cast<int>(EvsResult::INVALID_ARG));
+ }
+
+ // Update our internal state
+ if (!setAvailableFrames_Locked(bufferCount)) {
+ LOG(ERROR) << "Failed to adjust the maximum number of frames in flight.";
+ return ScopedAStatus::fromServiceSpecificError(
+ static_cast<int>(EvsResult::BUFFER_NOT_AVAILABLE));
+ }
+
+ return ScopedAStatus::ok();
+}
+
+ScopedAStatus EvsMockCamera::startVideoStream(const std::shared_ptr<IEvsCameraStream>& cb) {
+ LOG(DEBUG) << __FUNCTION__;
+
+ if (!cb) {
+ LOG(ERROR) << "A given stream callback is invalid.";
+ return ScopedAStatus::fromServiceSpecificError(static_cast<int>(EvsResult::INVALID_ARG));
+ }
+
+ std::lock_guard lock(mAccessLock);
+
+ // If we've been displaced by another owner of the camera, then we can't do anything else
+ if (mStreamState == DEAD) {
+ LOG(ERROR) << "Ignoring startVideoStream call when camera has been lost.";
+ return ScopedAStatus::fromServiceSpecificError(static_cast<int>(EvsResult::OWNERSHIP_LOST));
+ }
+
+ if (mStreamState != STOPPED) {
+ LOG(ERROR) << "Ignoring startVideoStream call when a stream is already running.";
+ return ScopedAStatus::fromServiceSpecificError(
+ static_cast<int>(EvsResult::STREAM_ALREADY_RUNNING));
+ }
+
+ // If the client never indicated otherwise, configure ourselves for a single streaming buffer
+ if (mFramesAllowed < kMinimumBuffersInFlight &&
+ !setAvailableFrames_Locked(kMinimumBuffersInFlight)) {
+ LOG(ERROR) << "Failed to start stream because we couldn't get a graphics buffer";
+ return ScopedAStatus::fromServiceSpecificError(
+ static_cast<int>(EvsResult::BUFFER_NOT_AVAILABLE));
+ }
+
+ // Record the user's callback for use when we have a frame ready
+ mStream = cb;
+
+ // Start the frame generation thread
+ mStreamState = RUNNING;
+ mCaptureThread = std::thread([this]() { generateFrames(); });
+
+ return ScopedAStatus::ok();
+}
+
+ScopedAStatus EvsMockCamera::doneWithFrame(const std::vector<BufferDesc>& list) {
+ std::lock_guard lock(mAccessLock);
+ for (const auto& desc : list) {
+ returnBufferLocked(desc.bufferId);
+ }
+
+ return ScopedAStatus::ok();
+}
+
+ScopedAStatus EvsMockCamera::stopVideoStream() {
+ LOG(DEBUG) << __FUNCTION__;
+ return stopVideoStream_impl();
+}
+
+ScopedAStatus EvsMockCamera::stopVideoStream_impl() {
+ std::unique_lock lock(mAccessLock);
+
+ if (mStreamState != RUNNING) {
+ // Safely return here because a stream is not running.
+ return ScopedAStatus::ok();
+ }
+
+ // Tell the GenerateFrames loop we want it to stop
+ mStreamState = STOPPING;
+
+ // Block outside the mutex until the "stop" flag has been acknowledged
+ // We won't send any more frames, but the client might still get some already in flight
+ LOG(DEBUG) << "Waiting for stream thread to end...";
+ lock.unlock();
+ if (mCaptureThread.joinable()) {
+ mCaptureThread.join();
+ }
+ lock.lock();
+
+ mStreamState = STOPPED;
+ mStream = nullptr;
+ LOG(DEBUG) << "Stream marked STOPPED.";
+
+ return ScopedAStatus::ok();
+}
+
+ScopedAStatus EvsMockCamera::getExtendedInfo(int32_t opaqueIdentifier,
+ std::vector<uint8_t>* opaqueValue) {
+ const auto it = mExtInfo.find(opaqueIdentifier);
+ if (it == mExtInfo.end()) {
+ return ScopedAStatus::fromServiceSpecificError(static_cast<int>(EvsResult::INVALID_ARG));
+ } else {
+ *opaqueValue = mExtInfo[opaqueIdentifier];
+ }
+
+ return ScopedAStatus::ok();
+}
+
+ScopedAStatus EvsMockCamera::setExtendedInfo(int32_t opaqueIdentifier,
+ const std::vector<uint8_t>& opaqueValue) {
+ mExtInfo.insert_or_assign(opaqueIdentifier, opaqueValue);
+ return ScopedAStatus::ok();
+}
+
+ScopedAStatus EvsMockCamera::getPhysicalCameraInfo([[maybe_unused]] const std::string& id,
+ CameraDesc* _aidl_return) {
+ LOG(DEBUG) << __FUNCTION__;
+
+ // This method works exactly same as getCameraInfo() in EVS HW module.
+ *_aidl_return = mDescription;
+ return ScopedAStatus::ok();
+}
+
+ScopedAStatus EvsMockCamera::pauseVideoStream() {
+ return ScopedAStatus::fromServiceSpecificError(static_cast<int>(EvsResult::NOT_SUPPORTED));
+}
+
+ScopedAStatus EvsMockCamera::resumeVideoStream() {
+ return ScopedAStatus::fromServiceSpecificError(static_cast<int>(EvsResult::NOT_SUPPORTED));
+}
+
+ScopedAStatus EvsMockCamera::setPrimaryClient() {
+ /* Because EVS HW module reference implementation expects a single client at
+ * a time, this returns a success code always.
+ */
+ return ScopedAStatus::ok();
+}
+
+ScopedAStatus EvsMockCamera::forcePrimaryClient(const std::shared_ptr<IEvsDisplay>&) {
+ /* Because EVS HW module reference implementation expects a single client at
+ * a time, this returns a success code always.
+ */
+ return ScopedAStatus::ok();
+}
+
+ScopedAStatus EvsMockCamera::unsetPrimaryClient() {
+ /* Because EVS HW module reference implementation expects a single client at
+ * a time, there is no chance that this is called by the secondary client and
+ * therefore returns a success code always.
+ */
+ return ScopedAStatus::ok();
+}
+
+ScopedAStatus EvsMockCamera::getParameterList(std::vector<CameraParam>* _aidl_return) {
+ if (mCameraInfo) {
+ _aidl_return->resize(mCameraInfo->controls.size());
+ auto idx = 0;
+ for (auto& [name, range] : mCameraInfo->controls) {
+ (*_aidl_return)[idx++] = name;
+ }
+ }
+
+ return ScopedAStatus::ok();
+}
+
+ScopedAStatus EvsMockCamera::getIntParameterRange(CameraParam id, ParameterRange* _aidl_return) {
+ auto it = mParams.find(id);
+ if (it == mParams.end()) {
+ return ScopedAStatus::fromServiceSpecificError(static_cast<int>(EvsResult::NOT_SUPPORTED));
+ }
+
+ _aidl_return->min = it->second->range.min;
+ _aidl_return->max = it->second->range.max;
+ _aidl_return->step = it->second->range.step;
+ return ScopedAStatus::ok();
+}
+
+ScopedAStatus EvsMockCamera::setIntParameter(CameraParam id, int32_t value,
+ std::vector<int32_t>* effectiveValue) {
+ auto it = mParams.find(id);
+ if (it == mParams.end()) {
+ return ScopedAStatus::fromServiceSpecificError(static_cast<int>(EvsResult::NOT_SUPPORTED));
+ }
+
+ // Rounding down to the closest value.
+ int32_t candidate = value / it->second->range.step * it->second->range.step;
+ if (candidate < it->second->range.min || candidate > it->second->range.max) {
+ return ScopedAStatus::fromServiceSpecificError(static_cast<int>(EvsResult::INVALID_ARG));
+ }
+
+ it->second->value = candidate;
+ effectiveValue->push_back(candidate);
+ return ScopedAStatus::ok();
+}
+
+ScopedAStatus EvsMockCamera::getIntParameter(CameraParam id, std::vector<int32_t>* value) {
+ auto it = mParams.find(id);
+ if (it == mParams.end()) {
+ return ScopedAStatus::fromServiceSpecificError(static_cast<int>(EvsResult::NOT_SUPPORTED));
+ }
+
+ value->push_back(it->second->value);
+ return ScopedAStatus::ok();
+}
+
+ScopedAStatus EvsMockCamera::importExternalBuffers(const std::vector<BufferDesc>& buffers,
+ int32_t* _aidl_return) {
+ size_t numBuffersToAdd = buffers.size();
+ if (numBuffersToAdd < 1) {
+ LOG(DEBUG) << "Ignoring a request to import external buffers with an empty list.";
+ return ScopedAStatus::ok();
+ }
+
+ std::lock_guard lock(mAccessLock);
+ if (numBuffersToAdd > (kMaxBuffersInFlight - mFramesAllowed)) {
+ numBuffersToAdd -= (kMaxBuffersInFlight - mFramesAllowed);
+ LOG(WARNING) << "Exceed the limit on the number of buffers. " << numBuffersToAdd
+ << " buffers will be imported only.";
+ }
+
+ ::android::GraphicBufferMapper& mapper = ::android::GraphicBufferMapper::get();
+ const size_t before = mFramesAllowed;
+ for (size_t i = 0; i < numBuffersToAdd; ++i) {
+ auto& b = buffers[i];
+ const AHardwareBuffer_Desc* pDesc =
+ reinterpret_cast<const AHardwareBuffer_Desc*>(&b.buffer.description);
+
+ buffer_handle_t handleToImport = ::android::dupFromAidl(b.buffer.handle);
+ buffer_handle_t handleToStore = nullptr;
+ if (handleToImport == nullptr) {
+ LOG(WARNING) << "Failed to duplicate a memory handle. Ignoring a buffer " << b.bufferId;
+ continue;
+ }
+
+ ::android::status_t result =
+ mapper.importBuffer(handleToImport, pDesc->width, pDesc->height, pDesc->layers,
+ pDesc->format, pDesc->usage, pDesc->stride, &handleToStore);
+ if (result != ::android::NO_ERROR || handleToStore == nullptr) {
+ LOG(WARNING) << "Failed to import a buffer " << b.bufferId;
+ continue;
+ }
+
+ bool stored = false;
+ for (auto&& rec : mBuffers) {
+ if (rec.handle != nullptr) {
+ continue;
+ }
+
+ // Use this existing entry.
+ rec.handle = handleToStore;
+ rec.inUse = false;
+ stored = true;
+ break;
+ }
+
+ if (!stored) {
+ // Add a BufferRecord wrapping this handle to our set of available buffers.
+ mBuffers.push_back(BufferRecord(handleToStore));
+ }
+ ++mFramesAllowed;
+ }
+
+ *_aidl_return = mFramesAllowed - before;
+ return ScopedAStatus::ok();
+}
+
+bool EvsMockCamera::setAvailableFrames_Locked(unsigned bufferCount) {
+ if (bufferCount < 1) {
+ LOG(ERROR) << "Ignoring request to set buffer count to zero";
+ return false;
+ }
+ if (bufferCount > kMaxBuffersInFlight) {
+ LOG(ERROR) << "Rejecting buffer request in excess of internal limit";
+ return false;
+ }
+
+ // Is an increase required?
+ if (mFramesAllowed < bufferCount) {
+ // An increase is required
+ auto needed = bufferCount - mFramesAllowed;
+ LOG(INFO) << "Allocating " << needed << " buffers for camera frames";
+
+ auto added = increaseAvailableFrames_Locked(needed);
+ if (added != needed) {
+ // If we didn't add all the frames we needed, then roll back to the previous state
+ LOG(ERROR) << "Rolling back to previous frame queue size";
+ decreaseAvailableFrames_Locked(added);
+ return false;
+ }
+ } else if (mFramesAllowed > bufferCount) {
+ // A decrease is required
+ auto framesToRelease = mFramesAllowed - bufferCount;
+ LOG(INFO) << "Returning " << framesToRelease << " camera frame buffers";
+
+ auto released = decreaseAvailableFrames_Locked(framesToRelease);
+ if (released != framesToRelease) {
+ // This shouldn't happen with a properly behaving client because the client
+ // should only make this call after returning sufficient outstanding buffers
+ // to allow a clean resize.
+ LOG(ERROR) << "Buffer queue shrink failed -- too many buffers currently in use?";
+ }
+ }
+
+ return true;
+}
+
+unsigned EvsMockCamera::increaseAvailableFrames_Locked(unsigned numToAdd) {
+ // Acquire the graphics buffer allocator
+ ::android::GraphicBufferAllocator& alloc(::android::GraphicBufferAllocator::get());
+
+ unsigned added = 0;
+ while (added < numToAdd) {
+ unsigned pixelsPerLine = 0;
+ buffer_handle_t memHandle = nullptr;
+ auto result = alloc.allocate(mWidth, mHeight, mFormat, 1, mUsage, &memHandle,
+ &pixelsPerLine, 0, "EvsMockCamera");
+ if (result != ::android::NO_ERROR) {
+ LOG(ERROR) << "Error " << result << " allocating " << mWidth << " x " << mHeight
+ << " graphics buffer";
+ break;
+ }
+ if (memHandle == nullptr) {
+ LOG(ERROR) << "We didn't get a buffer handle back from the allocator";
+ break;
+ }
+ if (mStride > 0) {
+ if (mStride != pixelsPerLine) {
+ LOG(ERROR) << "We did not expect to get buffers with different strides!";
+ }
+ } else {
+ // Gralloc defines stride in terms of pixels per line
+ mStride = pixelsPerLine;
+ }
+
+ // Find a place to store the new buffer
+ auto stored = false;
+ for (auto&& rec : mBuffers) {
+ if (rec.handle == nullptr) {
+ // Use this existing entry
+ rec.handle = memHandle;
+ rec.inUse = false;
+ stored = true;
+ break;
+ }
+ }
+ if (!stored) {
+ // Add a BufferRecord wrapping this handle to our set of available buffers
+ mBuffers.push_back(BufferRecord(memHandle));
+ }
+
+ ++mFramesAllowed;
+ ++added;
+ }
+
+ return added;
+}
+
+unsigned EvsMockCamera::decreaseAvailableFrames_Locked(unsigned numToRemove) {
+ // Acquire the graphics buffer allocator
+ ::android::GraphicBufferAllocator& alloc(::android::GraphicBufferAllocator::get());
+
+ unsigned removed = 0;
+ for (auto&& rec : mBuffers) {
+ // Is this record not in use, but holding a buffer that we can free?
+ if ((rec.inUse == false) && (rec.handle != nullptr)) {
+ // Release buffer and update the record so we can recognize it as "empty"
+ alloc.free(rec.handle);
+ rec.handle = nullptr;
+
+ --mFramesAllowed;
+ ++removed;
+
+ if (removed == numToRemove) {
+ break;
+ }
+ }
+ }
+
+ return removed;
+}
+
+// This is the asynchronous frame generation thread that runs in parallel with the
+// main serving thread. There is one for each active camera instance.
+void EvsMockCamera::generateFrames() {
+ LOG(DEBUG) << "Frame generation loop started.";
+
+ unsigned idx = 0;
+ while (true) {
+ bool timeForFrame = false;
+ const nsecs_t startTime = systemTime(SYSTEM_TIME_MONOTONIC);
+
+ // Lock scope for updating shared state
+ {
+ std::lock_guard lock(mAccessLock);
+
+ if (mStreamState != RUNNING) {
+ // Break out of our main thread loop
+ break;
+ }
+
+ // Are we allowed to issue another buffer?
+ if (mFramesInUse >= mFramesAllowed) {
+ // Can't do anything right now -- skip this frame
+ LOG(WARNING) << "Skipped a frame because too many are in flight.";
+ } else {
+ // Identify an available buffer to fill
+ for (idx = 0; idx < mBuffers.size(); idx++) {
+ if (!mBuffers[idx].inUse) {
+ if (mBuffers[idx].handle != nullptr) {
+ // Found an available record, so stop looking
+ break;
+ }
+ }
+ }
+ if (idx >= mBuffers.size()) {
+ // This shouldn't happen since we already checked mFramesInUse vs mFramesAllowed
+ ALOGE("Failed to find an available buffer slot\n");
+ } else {
+ // We're going to make the frame busy
+ mBuffers[idx].inUse = true;
+ mFramesInUse++;
+ timeForFrame = true;
+ }
+ }
+ }
+
+ if (timeForFrame) {
+ using AidlPixelFormat = ::aidl::android::hardware::graphics::common::PixelFormat;
+
+ // Assemble the buffer description we'll transmit below
+ buffer_handle_t memHandle = mBuffers[idx].handle;
+ BufferDesc newBuffer = {
+ .buffer =
+ {
+ .description =
+ {
+ .width = static_cast<int32_t>(mWidth),
+ .height = static_cast<int32_t>(mHeight),
+ .layers = 1,
+ .format = static_cast<AidlPixelFormat>(mFormat),
+ .usage = static_cast<BufferUsage>(mUsage),
+ .stride = static_cast<int32_t>(mStride),
+ },
+ .handle = ::android::dupToAidl(memHandle),
+ },
+ .bufferId = static_cast<int32_t>(idx),
+ .deviceId = mDescription.id,
+ .timestamp = static_cast<int64_t>(::android::elapsedRealtimeNano() *
+ 1e+3), // timestamps is in microseconds
+ };
+
+ // Write test data into the image buffer
+ fillMockFrame(memHandle, reinterpret_cast<const AHardwareBuffer_Desc*>(
+ &newBuffer.buffer.description));
+
+ // Issue the (asynchronous) callback to the client -- can't be holding the lock
+ auto flag = false;
+ if (mStream) {
+ std::vector<BufferDesc> frames;
+ frames.push_back(std::move(newBuffer));
+ flag = mStream->deliverFrame(frames).isOk();
+ }
+
+ if (flag) {
+ LOG(DEBUG) << "Delivered " << memHandle << ", id = " << mBuffers[idx].handle;
+ } else {
+ // This can happen if the client dies and is likely unrecoverable.
+ // To avoid consuming resources generating failing calls, we stop sending
+ // frames. Note, however, that the stream remains in the "STREAMING" state
+ // until cleaned up on the main thread.
+ LOG(ERROR) << "Frame delivery call failed in the transport layer.";
+
+ // Since we didn't actually deliver it, mark the frame as available
+ std::lock_guard<std::mutex> lock(mAccessLock);
+ mBuffers[idx].inUse = false;
+ mFramesInUse--;
+ }
+ }
+
+ // We arbitrarily choose to generate frames at 15 fps to ensure we pass the 10fps test
+ // requirement
+ static const int kTargetFrameRate = 15;
+ static const nsecs_t kTargetFrameIntervalUs = 1000 * 1000 / kTargetFrameRate;
+ const nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
+ const nsecs_t elapsedTimeUs = (now - startTime) / 1000;
+ const nsecs_t sleepDurationUs = kTargetFrameIntervalUs - elapsedTimeUs;
+ if (sleepDurationUs > 0) {
+ usleep(sleepDurationUs);
+ }
+ }
+
+ // If we've been asked to stop, send an event to signal the actual end of stream
+ EvsEventDesc event = {
+ .aType = EvsEventType::STREAM_STOPPED,
+ };
+ if (!mStream->notify(event).isOk()) {
+ ALOGE("Error delivering end of stream marker");
+ }
+
+ return;
+}
+
+void EvsMockCamera::fillMockFrame(buffer_handle_t handle, const AHardwareBuffer_Desc* pDesc) {
+ // Lock our output buffer for writing
+ uint32_t* pixels = nullptr;
+ ::android::GraphicBufferMapper& mapper = ::android::GraphicBufferMapper::get();
+ mapper.lock(handle, GRALLOC_USAGE_SW_WRITE_OFTEN | GRALLOC_USAGE_SW_READ_NEVER,
+ ::android::Rect(pDesc->width, pDesc->height), (void**)&pixels);
+
+ // If we failed to lock the pixel buffer, we're about to crash, but log it first
+ if (!pixels) {
+ ALOGE("Camera failed to gain access to image buffer for writing");
+ return;
+ }
+
+ // Fill in the test pixels; the colorbar in ABGR format
+ for (unsigned row = 0; row < pDesc->height; row++) {
+ for (unsigned col = 0; col < pDesc->width; col++) {
+ const uint32_t index = col * kNumColors / pDesc->width;
+ pixels[col] = kColors[index];
+ }
+ // Point to the next row
+ // NOTE: stride retrieved from gralloc is in units of pixels
+ pixels = pixels + pDesc->stride;
+ }
+
+ // Release our output buffer
+ mapper.unlock(handle);
+}
+
+void EvsMockCamera::returnBufferLocked(const uint32_t bufferId) {
+ if (bufferId >= mBuffers.size()) {
+ ALOGE("ignoring doneWithFrame called with invalid bufferId %d (max is %zu)", bufferId,
+ mBuffers.size() - 1);
+ return;
+ }
+
+ if (!mBuffers[bufferId].inUse) {
+ ALOGE("ignoring doneWithFrame called on frame %d which is already free", bufferId);
+ return;
+ }
+
+ // Mark the frame as available
+ mBuffers[bufferId].inUse = false;
+ mFramesInUse--;
+
+ // If this frame's index is high in the array, try to move it down
+ // to improve locality after mFramesAllowed has been reduced.
+ if (bufferId >= mFramesAllowed) {
+ // Find an empty slot lower in the array (which should always exist in this case)
+ for (auto&& rec : mBuffers) {
+ if (rec.handle == nullptr) {
+ rec.handle = mBuffers[bufferId].handle;
+ mBuffers[bufferId].handle = nullptr;
+ break;
+ }
+ }
+ }
+}
+
+std::shared_ptr<EvsMockCamera> EvsMockCamera::Create(const char* deviceName) {
+ std::unique_ptr<ConfigManager::CameraInfo> nullCamInfo = nullptr;
+
+ return Create(deviceName, nullCamInfo);
+}
+
+std::shared_ptr<EvsMockCamera> EvsMockCamera::Create(
+ const char* deviceName, std::unique_ptr<ConfigManager::CameraInfo>& camInfo,
+ [[maybe_unused]] const Stream* streamCfg) {
+ std::shared_ptr<EvsMockCamera> c =
+ ndk::SharedRefBase::make<EvsMockCamera>(Sigil{}, deviceName, camInfo);
+ if (!c) {
+ LOG(ERROR) << "Failed to instantiate EvsMockCamera.";
+ return nullptr;
+ }
+
+ // Use the first resolution from the list for the testing
+ // TODO(b/214835237): Uses a given Stream configuration to choose the best
+ // stream configuration.
+ auto it = camInfo->streamConfigurations.begin();
+ c->mWidth = it->second.width;
+ c->mHeight = it->second.height;
+ c->mDescription.vendorFlags = 0xFFFFFFFF; // Arbitrary test value
+
+ c->mFormat = HAL_PIXEL_FORMAT_RGBA_8888;
+ c->mUsage = GRALLOC_USAGE_HW_TEXTURE | GRALLOC_USAGE_HW_CAMERA_WRITE |
+ GRALLOC_USAGE_SW_READ_RARELY | GRALLOC_USAGE_SW_WRITE_RARELY;
+
+ return c;
+}
+
+} // namespace aidl::android::hardware::automotive::evs::implementation
diff --git a/automotive/evs/aidl/impl/default/src/GlWrapper.cpp b/automotive/evs/aidl/impl/default/src/GlWrapper.cpp
new file mode 100644
index 0000000..0ee5ecb
--- /dev/null
+++ b/automotive/evs/aidl/impl/default/src/GlWrapper.cpp
@@ -0,0 +1,465 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "GlWrapper.h"
+
+#include <aidl/android/frameworks/automotive/display/DisplayDesc.h>
+#include <aidl/android/hardware/graphics/common/HardwareBufferDescription.h>
+#include <aidlcommonsupport/NativeHandle.h>
+#include <ui/DisplayMode.h>
+#include <ui/DisplayState.h>
+#include <ui/GraphicBuffer.h>
+
+#include <fcntl.h>
+#include <stdio.h>
+#include <sys/ioctl.h>
+
+#include <utility>
+
+namespace {
+
+using ::aidl::android::frameworks::automotive::display::DisplayDesc;
+using ::aidl::android::frameworks::automotive::display::ICarDisplayProxy;
+using ::aidl::android::frameworks::automotive::display::Rotation;
+using ::aidl::android::hardware::common::NativeHandle;
+using ::aidl::android::hardware::graphics::common::HardwareBufferDescription;
+using ::android::GraphicBuffer;
+using ::android::sp;
+
+constexpr const char vertexShaderSource[] =
+ "attribute vec4 pos; \n"
+ "attribute vec2 tex; \n"
+ "varying vec2 uv; \n"
+ "void main() \n"
+ "{ \n"
+ " gl_Position = pos; \n"
+ " uv = tex; \n"
+ "} \n";
+
+constexpr const char pixelShaderSource[] =
+ "precision mediump float; \n"
+ "uniform sampler2D tex; \n"
+ "varying vec2 uv; \n"
+ "void main() \n"
+ "{ \n"
+ " gl_FragColor = texture2D(tex, uv);\n"
+ "} \n";
+
+const char* getEGLError(void) {
+ switch (eglGetError()) {
+ case EGL_SUCCESS:
+ return "EGL_SUCCESS";
+ case EGL_NOT_INITIALIZED:
+ return "EGL_NOT_INITIALIZED";
+ case EGL_BAD_ACCESS:
+ return "EGL_BAD_ACCESS";
+ case EGL_BAD_ALLOC:
+ return "EGL_BAD_ALLOC";
+ case EGL_BAD_ATTRIBUTE:
+ return "EGL_BAD_ATTRIBUTE";
+ case EGL_BAD_CONTEXT:
+ return "EGL_BAD_CONTEXT";
+ case EGL_BAD_CONFIG:
+ return "EGL_BAD_CONFIG";
+ case EGL_BAD_CURRENT_SURFACE:
+ return "EGL_BAD_CURRENT_SURFACE";
+ case EGL_BAD_DISPLAY:
+ return "EGL_BAD_DISPLAY";
+ case EGL_BAD_SURFACE:
+ return "EGL_BAD_SURFACE";
+ case EGL_BAD_MATCH:
+ return "EGL_BAD_MATCH";
+ case EGL_BAD_PARAMETER:
+ return "EGL_BAD_PARAMETER";
+ case EGL_BAD_NATIVE_PIXMAP:
+ return "EGL_BAD_NATIVE_PIXMAP";
+ case EGL_BAD_NATIVE_WINDOW:
+ return "EGL_BAD_NATIVE_WINDOW";
+ case EGL_CONTEXT_LOST:
+ return "EGL_CONTEXT_LOST";
+ default:
+ return "Unknown error";
+ }
+}
+
+// Given shader source, load and compile it
+GLuint loadShader(GLenum type, const char* shaderSrc) {
+ // Create the shader object
+ GLuint shader = glCreateShader(type);
+ if (shader == 0) {
+ return 0;
+ }
+
+ // Load and compile the shader
+ glShaderSource(shader, 1, &shaderSrc, nullptr);
+ glCompileShader(shader);
+
+ // Verify the compilation worked as expected
+ GLint compiled = 0;
+ glGetShaderiv(shader, GL_COMPILE_STATUS, &compiled);
+ if (!compiled) {
+ LOG(ERROR) << "Error compiling shader";
+
+ GLint size = 0;
+ glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &size);
+ if (size > 0) {
+ // Get and report the error message
+ char* infoLog = (char*)malloc(size);
+ glGetShaderInfoLog(shader, size, nullptr, infoLog);
+ LOG(ERROR) << " msg:" << std::endl << infoLog;
+ free(infoLog);
+ }
+
+ glDeleteShader(shader);
+ return 0;
+ }
+
+ return shader;
+}
+
+// Create a program object given vertex and pixels shader source
+GLuint buildShaderProgram(const char* vtxSrc, const char* pxlSrc) {
+ GLuint program = glCreateProgram();
+ if (program == 0) {
+ LOG(ERROR) << "Failed to allocate program object";
+ return 0;
+ }
+
+ // Compile the shaders and bind them to this program
+ GLuint vertexShader = loadShader(GL_VERTEX_SHADER, vtxSrc);
+ if (vertexShader == 0) {
+ LOG(ERROR) << "Failed to load vertex shader";
+ glDeleteProgram(program);
+ return 0;
+ }
+ GLuint pixelShader = loadShader(GL_FRAGMENT_SHADER, pxlSrc);
+ if (pixelShader == 0) {
+ LOG(ERROR) << "Failed to load pixel shader";
+ glDeleteProgram(program);
+ glDeleteShader(vertexShader);
+ return 0;
+ }
+ glAttachShader(program, vertexShader);
+ glAttachShader(program, pixelShader);
+
+ glBindAttribLocation(program, 0, "pos");
+ glBindAttribLocation(program, 1, "tex");
+
+ // Link the program
+ glLinkProgram(program);
+ GLint linked = 0;
+ glGetProgramiv(program, GL_LINK_STATUS, &linked);
+ if (!linked) {
+ LOG(ERROR) << "Error linking program";
+ GLint size = 0;
+ glGetProgramiv(program, GL_INFO_LOG_LENGTH, &size);
+ if (size > 0) {
+ // Get and report the error message
+ char* infoLog = (char*)malloc(size);
+ glGetProgramInfoLog(program, size, nullptr, infoLog);
+ LOG(ERROR) << " msg: " << infoLog;
+ free(infoLog);
+ }
+
+ glDeleteProgram(program);
+ glDeleteShader(vertexShader);
+ glDeleteShader(pixelShader);
+ return 0;
+ }
+
+ return program;
+}
+
+::android::sp<HGraphicBufferProducer> convertNativeHandleToHGBP(const NativeHandle& aidlHandle) {
+ native_handle_t* handle = ::android::dupFromAidl(aidlHandle);
+ if (handle->numFds != 0 || handle->numInts < std::ceil(sizeof(size_t) / sizeof(int))) {
+ LOG(ERROR) << "Invalid native handle";
+ return nullptr;
+ }
+ ::android::hardware::hidl_vec<uint8_t> halToken;
+ halToken.setToExternal(reinterpret_cast<uint8_t*>(const_cast<int*>(&(handle->data[1]))),
+ handle->data[0]);
+ ::android::sp<HGraphicBufferProducer> hgbp =
+ HGraphicBufferProducer::castFrom(::android::retrieveHalInterface(halToken));
+ return std::move(hgbp);
+}
+
+} // namespace
+
+namespace aidl::android::hardware::automotive::evs::implementation {
+
+// Main entry point
+bool GlWrapper::initialize(const std::shared_ptr<ICarDisplayProxy>& pWindowProxy,
+ uint64_t displayId) {
+ LOG(DEBUG) << __FUNCTION__;
+
+ if (!pWindowProxy) {
+ LOG(ERROR) << "Could not get ICarDisplayProxy.";
+ return false;
+ }
+
+ DisplayDesc displayDesc;
+ auto status = pWindowProxy->getDisplayInfo(displayId, &displayDesc);
+ if (!status.isOk()) {
+ LOG(ERROR) << "Failed to read the display information";
+ return false;
+ }
+
+ mWidth = displayDesc.width;
+ mHeight = displayDesc.height;
+ if ((displayDesc.orientation != Rotation::ROTATION_0) &&
+ (displayDesc.orientation != Rotation::ROTATION_180)) {
+ std::swap(mWidth, mHeight);
+ }
+ LOG(INFO) << "Display resolution is " << mWidth << "x" << mHeight;
+
+ NativeHandle aidlHandle;
+ status = pWindowProxy->getHGraphicBufferProducer(displayId, &aidlHandle);
+ if (!status.isOk()) {
+ LOG(ERROR) << "Failed to get IGraphicBufferProducer from ICarDisplayProxy.";
+ return false;
+ }
+
+ mGfxBufferProducer = convertNativeHandleToHGBP(aidlHandle);
+ if (!mGfxBufferProducer) {
+ LOG(ERROR) << "Failed to convert a NativeHandle to HGBP.";
+ return false;
+ }
+
+ mSurfaceHolder = getSurfaceFromHGBP(mGfxBufferProducer);
+ if (mSurfaceHolder == nullptr) {
+ LOG(ERROR) << "Failed to get a Surface from HGBP.";
+ return false;
+ }
+
+ mWindow = getNativeWindow(mSurfaceHolder.get());
+ if (mWindow == nullptr) {
+ LOG(ERROR) << "Failed to get a native window from Surface.";
+ return false;
+ }
+
+ // Set up our OpenGL ES context associated with the default display
+ mDisplay = eglGetDisplay(EGL_DEFAULT_DISPLAY);
+ if (mDisplay == EGL_NO_DISPLAY) {
+ LOG(ERROR) << "Failed to get egl display";
+ return false;
+ }
+
+ EGLint major = 2;
+ EGLint minor = 0;
+ if (!eglInitialize(mDisplay, &major, &minor)) {
+ LOG(ERROR) << "Failed to initialize EGL: " << getEGLError();
+ return false;
+ }
+
+ const EGLint config_attribs[] = {
+ // clang-format off
+ // Tag Value
+ EGL_RED_SIZE, 8,
+ EGL_GREEN_SIZE, 8,
+ EGL_BLUE_SIZE, 8,
+ EGL_DEPTH_SIZE, 0,
+ EGL_NONE
+ // clang-format on
+ };
+
+ // Pick the default configuration without constraints (is this good enough?)
+ EGLConfig egl_config = {0};
+ EGLint numConfigs = -1;
+ eglChooseConfig(mDisplay, config_attribs, &egl_config, 1, &numConfigs);
+ if (numConfigs != 1) {
+ LOG(ERROR) << "Didn't find a suitable format for our display window, " << getEGLError();
+ return false;
+ }
+
+ // Create the EGL render target surface
+ mSurface = eglCreateWindowSurface(mDisplay, egl_config, mWindow, nullptr);
+ if (mSurface == EGL_NO_SURFACE) {
+ LOG(ERROR) << "eglCreateWindowSurface failed, " << getEGLError();
+ return false;
+ }
+
+ // Create the EGL context
+ // NOTE: Our shader is (currently at least) written to require version 3, so this
+ // is required.
+ const EGLint context_attribs[] = {EGL_CONTEXT_CLIENT_VERSION, 3, EGL_NONE};
+ mContext = eglCreateContext(mDisplay, egl_config, EGL_NO_CONTEXT, context_attribs);
+ if (mContext == EGL_NO_CONTEXT) {
+ LOG(ERROR) << "Failed to create OpenGL ES Context: " << getEGLError();
+ return false;
+ }
+
+ // Activate our render target for drawing
+ if (!eglMakeCurrent(mDisplay, mSurface, mSurface, mContext)) {
+ LOG(ERROR) << "Failed to make the OpenGL ES Context current: " << getEGLError();
+ return false;
+ }
+
+ // Create the shader program for our simple pipeline
+ mShaderProgram = buildShaderProgram(vertexShaderSource, pixelShaderSource);
+ if (!mShaderProgram) {
+ LOG(ERROR) << "Failed to build shader program: " << getEGLError();
+ return false;
+ }
+
+ // Create a GL texture that will eventually wrap our externally created texture surface(s)
+ glGenTextures(1, &mTextureMap);
+ if (mTextureMap <= 0) {
+ LOG(ERROR) << "Didn't get a texture handle allocated: " << getEGLError();
+ return false;
+ }
+
+ // Turn off mip-mapping for the created texture surface
+ // (the inbound camera imagery doesn't have MIPs)
+ glBindTexture(GL_TEXTURE_2D, mTextureMap);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+ glBindTexture(GL_TEXTURE_2D, 0);
+
+ return true;
+}
+
+void GlWrapper::shutdown() {
+ // Drop our device textures
+ if (mKHRimage != EGL_NO_IMAGE_KHR) {
+ eglDestroyImageKHR(mDisplay, mKHRimage);
+ mKHRimage = EGL_NO_IMAGE_KHR;
+ }
+
+ // Release all GL resources
+ if (eglGetCurrentContext() == mContext) {
+ eglMakeCurrent(mDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
+ }
+ eglDestroySurface(mDisplay, mSurface);
+ eglDestroyContext(mDisplay, mContext);
+ eglTerminate(mDisplay);
+ mSurface = EGL_NO_SURFACE;
+ mContext = EGL_NO_CONTEXT;
+ mDisplay = EGL_NO_DISPLAY;
+
+ // Release the window
+ mSurfaceHolder = nullptr;
+}
+
+void GlWrapper::showWindow(const std::shared_ptr<ICarDisplayProxy>& pWindowProxy, uint64_t id) {
+ if (pWindowProxy) {
+ pWindowProxy->showWindow(id);
+ } else {
+ LOG(ERROR) << "ICarDisplayProxy is not available.";
+ }
+}
+
+void GlWrapper::hideWindow(const std::shared_ptr<ICarDisplayProxy>& pWindowProxy, uint64_t id) {
+ if (pWindowProxy) {
+ pWindowProxy->hideWindow(id);
+ } else {
+ LOG(ERROR) << "ICarDisplayProxy is not available.";
+ }
+}
+
+bool GlWrapper::updateImageTexture(buffer_handle_t handle,
+ const HardwareBufferDescription& description) {
+ if (mKHRimage != EGL_NO_IMAGE_KHR) {
+ return true;
+ }
+
+ // Create a temporary GraphicBuffer to wrap the provided handle.
+ sp<GraphicBuffer> pGfxBuffer =
+ new GraphicBuffer(description.width, description.height,
+ static_cast<::android::PixelFormat>(description.format),
+ description.layers, static_cast<uint32_t>(description.usage),
+ description.stride, const_cast<native_handle_t*>(handle),
+ /* keepOwnership= */ false);
+ if (!pGfxBuffer) {
+ LOG(ERROR) << "Failed to allocate GraphicBuffer to wrap our native handle";
+ return false;
+ }
+
+ // Get a GL compatible reference to the graphics buffer we've been given
+ EGLint eglImageAttributes[] = {EGL_IMAGE_PRESERVED_KHR, EGL_TRUE, EGL_NONE};
+ EGLClientBuffer cbuf = static_cast<EGLClientBuffer>(pGfxBuffer->getNativeBuffer());
+ mKHRimage = eglCreateImageKHR(mDisplay, EGL_NO_CONTEXT, EGL_NATIVE_BUFFER_ANDROID, cbuf,
+ eglImageAttributes);
+ if (mKHRimage == EGL_NO_IMAGE_KHR) {
+ LOG(ERROR) << "Error creating EGLImage: " << getEGLError();
+ return false;
+ }
+
+ // Update the texture handle we already created to refer to this gralloc buffer
+ glActiveTexture(GL_TEXTURE0);
+ glBindTexture(GL_TEXTURE_2D, mTextureMap);
+ glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, static_cast<GLeglImageOES>(mKHRimage));
+
+ return true;
+}
+
+void GlWrapper::renderImageToScreen() {
+ // Set the viewport
+ glViewport(0, 0, mWidth, mHeight);
+
+ // Clear the color buffer
+ glClearColor(0.1f, 0.5f, 0.1f, 1.0f);
+ glClear(GL_COLOR_BUFFER_BIT);
+
+ // Select our screen space simple texture shader
+ glUseProgram(mShaderProgram);
+
+ // Bind the texture and assign it to the shader's sampler
+ glActiveTexture(GL_TEXTURE0);
+ glBindTexture(GL_TEXTURE_2D, mTextureMap);
+ GLint sampler = glGetUniformLocation(mShaderProgram, "tex");
+ glUniform1i(sampler, 0);
+
+ // We want our image to show up opaque regardless of alpha values
+ glDisable(GL_BLEND);
+
+ // Draw a rectangle on the screen
+ GLfloat vertsCarPos[] = {
+ // clang-format off
+ -0.8, 0.8, 0.0f, // left top in window space
+ 0.8, 0.8, 0.0f, // right top
+ -0.8, -0.8, 0.0f, // left bottom
+ 0.8, -0.8, 0.0f // right bottom
+ // clang-format on
+ };
+
+ // NOTE: We didn't flip the image in the texture, so V=0 is actually the top of the image
+ GLfloat vertsCarTex[] = {
+ // clang-format off
+ 0.0f, 0.0f, // left top
+ 1.0f, 0.0f, // right top
+ 0.0f, 1.0f, // left bottom
+ 1.0f, 1.0f // right bottom
+ // clang-format on
+ };
+ glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, vertsCarPos);
+ glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 0, vertsCarTex);
+ glEnableVertexAttribArray(0);
+ glEnableVertexAttribArray(1);
+
+ glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
+
+ // Clean up and flip the rendered result to the front so it is visible
+ glDisableVertexAttribArray(0);
+ glDisableVertexAttribArray(1);
+
+ glFinish();
+
+ if (eglSwapBuffers(mDisplay, mSurface) == EGL_FALSE) {
+ LOG(WARNING) << "Failed to swap EGL buffers, " << getEGLError();
+ }
+}
+
+} // namespace aidl::android::hardware::automotive::evs::implementation
diff --git a/automotive/evs/aidl/impl/default/src/service.cpp b/automotive/evs/aidl/impl/default/src/service.cpp
index 0a0913f..7532d87 100644
--- a/automotive/evs/aidl/impl/default/src/service.cpp
+++ b/automotive/evs/aidl/impl/default/src/service.cpp
@@ -14,38 +14,75 @@
* limitations under the License.
*/
-#define LOG_TAG "EvsService"
-
-#include <DefaultEvsEnumerator.h>
+#include "EvsEnumerator.h"
+#include "EvsGlDisplay.h"
#include <android/binder_manager.h>
#include <android/binder_process.h>
#include <utils/Log.h>
-using ::aidl::android::hardware::automotive::evs::implementation::DefaultEvsEnumerator;
+#include <unistd.h>
-int main([[maybe_unused]] int argc, [[maybe_unused]] char* argv[]) {
- std::shared_ptr<DefaultEvsEnumerator> vhal = ndk::SharedRefBase::make<DefaultEvsEnumerator>();
+#include <atomic>
+#include <cstdlib>
+#include <string_view>
- ALOGI("Registering as service...");
- binder_exception_t err =
- AServiceManager_addService(vhal->asBinder().get(), "android.hardware.automotive.evs");
+namespace {
+
+using ::aidl::android::frameworks::automotive::display::ICarDisplayProxy;
+using ::aidl::android::hardware::automotive::evs::implementation::EvsEnumerator;
+
+constexpr std::string_view kDisplayServiceInstanceName = "/default";
+constexpr std::string_view kHwInstanceName = "/hw/0";
+constexpr int kNumBinderThreads = 1;
+
+} // namespace
+
+int main() {
+ LOG(INFO) << "EVS Hardware Enumerator service is starting";
+
+ const std::string displayServiceInstanceName =
+ std::string(ICarDisplayProxy::descriptor) + std::string(kDisplayServiceInstanceName);
+ if (!AServiceManager_isDeclared(displayServiceInstanceName.data())) {
+ // TODO: We may just want to disable EVS display.
+ LOG(ERROR) << displayServiceInstanceName << " is required.";
+ return EXIT_FAILURE;
+ }
+
+ std::shared_ptr<ICarDisplayProxy> displayService = ICarDisplayProxy::fromBinder(
+ ::ndk::SpAIBinder(AServiceManager_waitForService(displayServiceInstanceName.data())));
+ if (!displayService) {
+ LOG(ERROR) << "Cannot use " << displayServiceInstanceName << ". Exiting.";
+ return EXIT_FAILURE;
+ }
+
+ // Register our service -- if somebody is already registered by our name,
+ // they will be killed (their thread pool will throw an exception).
+ std::shared_ptr<EvsEnumerator> service =
+ ndk::SharedRefBase::make<EvsEnumerator>(displayService);
+ if (!service) {
+ LOG(ERROR) << "Failed to instantiate the service";
+ return EXIT_FAILURE;
+ }
+
+ const std::string instanceName =
+ std::string(EvsEnumerator::descriptor) + std::string(kHwInstanceName);
+ auto err = AServiceManager_addService(service->asBinder().get(), instanceName.data());
if (err != EX_NONE) {
- ALOGE("failed to register android.hardware.automotive.evs service, exception: %d", err);
- return 1;
+ LOG(ERROR) << "Failed to register " << instanceName << ", exception = " << err;
+ return EXIT_FAILURE;
}
- if (!ABinderProcess_setThreadPoolMaxThreadCount(1)) {
- ALOGE("%s", "failed to set thread pool max thread count");
- return 1;
+ if (!ABinderProcess_setThreadPoolMaxThreadCount(kNumBinderThreads)) {
+ LOG(ERROR) << "Failed to set thread pool";
+ return EXIT_FAILURE;
}
+
ABinderProcess_startThreadPool();
-
- ALOGI("Evs Service Ready");
+ LOG(INFO) << "EVS Hardware Enumerator is ready";
ABinderProcess_joinThreadPool();
-
- ALOGI("Evs Service Exiting");
-
- return 0;
+ // In normal operation, we don't expect the thread pool to exit
+ LOG(INFO) << "EVS Hardware Enumerator is shutting down";
+ return EXIT_SUCCESS;
}
diff --git a/automotive/evs/aidl/vts/Android.bp b/automotive/evs/aidl/vts/Android.bp
index 5aa9501..e50c913 100644
--- a/automotive/evs/aidl/vts/Android.bp
+++ b/automotive/evs/aidl/vts/Android.bp
@@ -14,18 +14,17 @@
// limitations under the License.
//
-package{
+package {
// See: http://go/android-license-faq
// A large-scale-change added 'default_applicable_licenses' to import
// all of the 'license_kinds' from "hardware_interfaces_license"
// to get the below license kinds:
// SPDX-license-identifier-Apache-2.0
- default_applicable_licenses : ["hardware_interfaces_license"],
+ default_applicable_licenses: ["hardware_interfaces_license"],
}
cc_test {
-name:
- "VtsHalEvsTargetTest",
+ name: "VtsHalEvsTargetTest",
srcs: [
"*.cpp",
],
@@ -42,7 +41,7 @@
],
static_libs: [
"android.hardware.automotive.evs@common-default-lib",
- "android.hardware.automotive.evs-V1-ndk",
+ "android.hardware.automotive.evs-V2-ndk",
"android.hardware.common-V2-ndk",
"libaidlcommonsupport",
],
diff --git a/automotive/evs/aidl/vts/VtsHalEvsTargetTest.cpp b/automotive/evs/aidl/vts/VtsHalEvsTargetTest.cpp
index 2706c49..a6d99ad 100644
--- a/automotive/evs/aidl/vts/VtsHalEvsTargetTest.cpp
+++ b/automotive/evs/aidl/vts/VtsHalEvsTargetTest.cpp
@@ -51,6 +51,7 @@
#include <ui/GraphicBufferAllocator.h>
#include <utils/Timers.h>
+#include <chrono>
#include <deque>
#include <thread>
#include <unordered_set>
@@ -2197,6 +2198,203 @@
}
}
+/*
+ * DisplayOpen:
+ * Test both clean shut down and "aggressive open" device stealing behavior.
+ */
+TEST_P(EvsAidlTest, DisplayOpen) {
+ LOG(INFO) << "Starting DisplayOpen test";
+
+ // Request available display IDs.
+ std::vector<uint8_t> displayIds;
+ ASSERT_TRUE(mEnumerator->getDisplayIdList(&displayIds).isOk());
+ EXPECT_GT(displayIds.size(), 0);
+
+ for (const auto displayId : displayIds) {
+ std::shared_ptr<IEvsDisplay> pDisplay;
+
+ // Request exclusive access to each EVS display, then let it go.
+ ASSERT_TRUE(mEnumerator->openDisplay(displayId, &pDisplay).isOk());
+ ASSERT_NE(pDisplay, nullptr);
+
+ {
+ // Ask the display what its name is.
+ DisplayDesc desc;
+ ASSERT_TRUE(pDisplay->getDisplayInfo(&desc).isOk());
+ LOG(DEBUG) << "Found display " << desc.id;
+ }
+
+ ASSERT_TRUE(mEnumerator->closeDisplay(pDisplay).isOk());
+
+ // Ensure we can reopen the display after it has been closed.
+ ASSERT_TRUE(mEnumerator->openDisplay(displayId, &pDisplay).isOk());
+ ASSERT_NE(pDisplay, nullptr);
+
+ // Open the display while its already open -- ownership should be transferred.
+ std::shared_ptr<IEvsDisplay> pDisplay2;
+ ASSERT_TRUE(mEnumerator->openDisplay(displayId, &pDisplay2).isOk());
+ ASSERT_NE(pDisplay2, nullptr);
+
+ {
+ // Ensure the old display properly reports its assassination.
+ DisplayState badState;
+ EXPECT_TRUE(pDisplay->getDisplayState(&badState).isOk());
+ EXPECT_EQ(badState, DisplayState::DEAD);
+ }
+
+ // Close only the newest display instance -- the other should already be a zombie.
+ ASSERT_TRUE(mEnumerator->closeDisplay(pDisplay2).isOk());
+
+ // Finally, validate that we can open the display after the provoked failure above.
+ ASSERT_TRUE(mEnumerator->openDisplay(displayId, &pDisplay).isOk());
+ ASSERT_NE(pDisplay, nullptr);
+ ASSERT_TRUE(mEnumerator->closeDisplay(pDisplay).isOk());
+ }
+}
+
+/*
+ * DisplayStates:
+ * Validate that display states transition as expected and can be queried from either the display
+ * object itself or the owning enumerator.
+ */
+TEST_P(EvsAidlTest, DisplayStates) {
+ using std::literals::chrono_literals::operator""ms;
+
+ LOG(INFO) << "Starting DisplayStates test";
+
+ // Request available display IDs.
+ std::vector<uint8_t> displayIds;
+ ASSERT_TRUE(mEnumerator->getDisplayIdList(&displayIds).isOk());
+ EXPECT_GT(displayIds.size(), 0);
+
+ for (const auto displayId : displayIds) {
+ // Ensure the display starts in the expected state.
+ {
+ DisplayState state;
+ EXPECT_FALSE(mEnumerator->getDisplayState(&state).isOk());
+ }
+ for (const auto displayIdToQuery : displayIds) {
+ DisplayState state;
+ EXPECT_FALSE(mEnumerator->getDisplayStateById(displayIdToQuery, &state).isOk());
+ }
+
+ // Scope to limit the lifetime of the pDisplay pointer, and thus the IEvsDisplay object.
+ {
+ // Request exclusive access to the EVS display.
+ std::shared_ptr<IEvsDisplay> pDisplay;
+ ASSERT_TRUE(mEnumerator->openDisplay(displayId, &pDisplay).isOk());
+ ASSERT_NE(pDisplay, nullptr);
+ {
+ DisplayState state;
+ EXPECT_TRUE(mEnumerator->getDisplayState(&state).isOk());
+ EXPECT_EQ(state, DisplayState::NOT_VISIBLE);
+ }
+ for (const auto displayIdToQuery : displayIds) {
+ DisplayState state;
+ if (displayIdToQuery == displayId) {
+ EXPECT_TRUE(mEnumerator->getDisplayStateById(displayIdToQuery, &state).isOk());
+ EXPECT_EQ(state, DisplayState::NOT_VISIBLE);
+ } else {
+ EXPECT_FALSE(mEnumerator->getDisplayStateById(displayIdToQuery, &state).isOk());
+ }
+ }
+
+ // Activate the display.
+ EXPECT_TRUE(pDisplay->setDisplayState(DisplayState::VISIBLE_ON_NEXT_FRAME).isOk());
+ {
+ DisplayState state;
+ EXPECT_TRUE(mEnumerator->getDisplayState(&state).isOk());
+ EXPECT_EQ(state, DisplayState::VISIBLE_ON_NEXT_FRAME);
+ }
+ {
+ DisplayState state;
+ EXPECT_TRUE(pDisplay->getDisplayState(&state).isOk());
+ EXPECT_EQ(state, DisplayState::VISIBLE_ON_NEXT_FRAME);
+ }
+ for (const auto displayIdToQuery : displayIds) {
+ DisplayState state;
+ if (displayIdToQuery == displayId) {
+ EXPECT_TRUE(mEnumerator->getDisplayStateById(displayIdToQuery, &state).isOk());
+ EXPECT_EQ(state, DisplayState::VISIBLE_ON_NEXT_FRAME);
+ } else {
+ EXPECT_FALSE(mEnumerator->getDisplayStateById(displayIdToQuery, &state).isOk());
+ }
+ }
+
+ // Get the output buffer we'd use to display the imagery.
+ BufferDesc tgtBuffer;
+ ASSERT_TRUE(pDisplay->getTargetBuffer(&tgtBuffer).isOk());
+
+ // Send the target buffer back for display (we didn't actually fill anything).
+ EXPECT_TRUE(pDisplay->returnTargetBufferForDisplay(tgtBuffer).isOk());
+
+ // Sleep for a tenth of a second to ensure the driver has time to get the image
+ // displayed.
+ std::this_thread::sleep_for(100ms);
+ {
+ DisplayState state;
+ EXPECT_TRUE(mEnumerator->getDisplayState(&state).isOk());
+ EXPECT_EQ(state, DisplayState::VISIBLE);
+ }
+ {
+ DisplayState state;
+ EXPECT_TRUE(pDisplay->getDisplayState(&state).isOk());
+ EXPECT_EQ(state, DisplayState::VISIBLE);
+ }
+ for (const auto displayIdToQuery : displayIds) {
+ DisplayState state;
+ if (displayIdToQuery == displayId) {
+ EXPECT_TRUE(mEnumerator->getDisplayStateById(displayIdToQuery, &state).isOk());
+ EXPECT_EQ(state, DisplayState::VISIBLE);
+ } else {
+ EXPECT_FALSE(mEnumerator->getDisplayStateById(displayIdToQuery, &state).isOk());
+ }
+ }
+
+ // Turn off the display.
+ EXPECT_TRUE(pDisplay->setDisplayState(DisplayState::NOT_VISIBLE).isOk());
+ std::this_thread::sleep_for(100ms);
+ {
+ DisplayState state;
+ EXPECT_TRUE(mEnumerator->getDisplayState(&state).isOk());
+ EXPECT_EQ(state, DisplayState::NOT_VISIBLE);
+ }
+ {
+ DisplayState state;
+ EXPECT_TRUE(pDisplay->getDisplayState(&state).isOk());
+ EXPECT_EQ(state, DisplayState::NOT_VISIBLE);
+ }
+ for (const auto displayIdToQuery : displayIds) {
+ DisplayState state;
+ if (displayIdToQuery == displayId) {
+ EXPECT_TRUE(mEnumerator->getDisplayStateById(displayIdToQuery, &state).isOk());
+ EXPECT_EQ(state, DisplayState::NOT_VISIBLE);
+ } else {
+ EXPECT_FALSE(mEnumerator->getDisplayStateById(displayIdToQuery, &state).isOk());
+ }
+ }
+
+ // Close the display.
+ mEnumerator->closeDisplay(pDisplay);
+ }
+
+ // Now that the display pointer has gone out of scope, causing the IEvsDisplay interface
+ // object to be destroyed, we should be back to the "not open" state.
+ // NOTE: If we want this to pass without the sleep above, we'd have to add the
+ // (now recommended) closeDisplay() call instead of relying on the smarter pointer
+ // going out of scope. I've not done that because I want to verify that the deletion
+ // of the object does actually clean up (eventually).
+ {
+ DisplayState state;
+ EXPECT_FALSE(mEnumerator->getDisplayState(&state).isOk());
+ }
+ for (const auto displayIdToQuery : displayIds) {
+ DisplayState state;
+ EXPECT_FALSE(mEnumerator->getDisplayStateById(displayIdToQuery, &state).isOk());
+ }
+ }
+}
+
GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(EvsAidlTest);
INSTANTIATE_TEST_SUITE_P(
PerInstance, EvsAidlTest,
diff --git a/automotive/occupant_awareness/aidl/default/Android.bp b/automotive/occupant_awareness/aidl/default/Android.bp
index 1b760a5..dc280df 100644
--- a/automotive/occupant_awareness/aidl/default/Android.bp
+++ b/automotive/occupant_awareness/aidl/default/Android.bp
@@ -26,6 +26,7 @@
cc_binary {
name: "android.hardware.automotive.occupant_awareness@1.0-service",
init_rc: ["android.hardware.automotive.occupant_awareness@1.0-service.rc"],
+ vintf_fragments: ["android.hardware.automotive.occupant_awareness-service.xml"],
relative_install_path: "hw",
vendor: true,
srcs: [
diff --git a/automotive/occupant_awareness/aidl/default/android.hardware.automotive.occupant_awareness-service.xml b/automotive/occupant_awareness/aidl/default/android.hardware.automotive.occupant_awareness-service.xml
new file mode 100644
index 0000000..b4f8fa5
--- /dev/null
+++ b/automotive/occupant_awareness/aidl/default/android.hardware.automotive.occupant_awareness-service.xml
@@ -0,0 +1,7 @@
+<manifest version="1.0" type="device">
+ <hal format="aidl">
+ <name>android.hardware.automotive.occupant_awareness</name>
+ <version>1</version>
+ <fqname>IOccupantAwareness/default</fqname>
+ </hal>
+</manifest>
diff --git a/automotive/remoteaccess/aidl_api/android.hardware.automotive.remoteaccess/current/android/hardware/automotive/remoteaccess/IRemoteAccess.aidl b/automotive/remoteaccess/aidl_api/android.hardware.automotive.remoteaccess/current/android/hardware/automotive/remoteaccess/IRemoteAccess.aidl
index 9b6eb2f..b0935c2 100644
--- a/automotive/remoteaccess/aidl_api/android.hardware.automotive.remoteaccess/current/android/hardware/automotive/remoteaccess/IRemoteAccess.aidl
+++ b/automotive/remoteaccess/aidl_api/android.hardware.automotive.remoteaccess/current/android/hardware/automotive/remoteaccess/IRemoteAccess.aidl
@@ -34,8 +34,9 @@
package android.hardware.automotive.remoteaccess;
@VintfStability
interface IRemoteAccess {
- String getDeviceId();
+ String getVehicleId();
String getWakeupServiceName();
+ String getProcessorId();
void setRemoteTaskCallback(android.hardware.automotive.remoteaccess.IRemoteTaskCallback callback);
void clearRemoteTaskCallback();
void notifyApStateChange(in android.hardware.automotive.remoteaccess.ApState state);
diff --git a/automotive/remoteaccess/android/hardware/automotive/remoteaccess/IRemoteAccess.aidl b/automotive/remoteaccess/android/hardware/automotive/remoteaccess/IRemoteAccess.aidl
index a198b03..0f4125f 100644
--- a/automotive/remoteaccess/android/hardware/automotive/remoteaccess/IRemoteAccess.aidl
+++ b/automotive/remoteaccess/android/hardware/automotive/remoteaccess/IRemoteAccess.aidl
@@ -28,19 +28,19 @@
@VintfStability
interface IRemoteAccess {
/**
- * Gets a unique device ID that could be recognized by wake up server.
+ * Gets a unique vehicle ID that could be recognized by wake up server.
*
- * This device ID is provisioned during car production and is registered
+ * <p>This vehicle ID is provisioned during car production and is registered
* with the wake up server.
*
- * @return a unique device ID.
+ * @return a unique vehicle ID.
*/
- String getDeviceId();
+ String getVehicleId();
/**
* Gets the name for the remote wakeup server.
*
- * This name will be provided to remote task server during registration
+ * <p>This name will be provided to remote task server during registration
* and used by remote task server to find the remote wakeup server to
* use for waking up the device. This name must be pre-negotiated between
* the remote wakeup server/client and the remote task server/client and
@@ -51,6 +51,17 @@
String getWakeupServiceName();
/**
+ * Gets a unique processor ID that could be recognized by wake up client.
+ *
+ * <p>This processor ID is used to identify each processor in the vehicle.
+ * The wake up client which handles many processors determines which
+ * processor to wake up from the processor ID.
+ *
+ * <p> The processor ID must be unique in the vehicle.
+ */
+ String getProcessorId();
+
+ /**
* Sets a callback to be called when a remote task is requested.
*
* @param callback A callback to be called when a remote task is requested.
diff --git a/automotive/remoteaccess/hal/default/include/RemoteAccessService.h b/automotive/remoteaccess/hal/default/include/RemoteAccessService.h
index 74c2af4..9aabad6 100644
--- a/automotive/remoteaccess/hal/default/include/RemoteAccessService.h
+++ b/automotive/remoteaccess/hal/default/include/RemoteAccessService.h
@@ -62,7 +62,9 @@
~RemoteAccessService();
- ndk::ScopedAStatus getDeviceId(std::string* deviceId) override;
+ ndk::ScopedAStatus getVehicleId(std::string* vehicleId) override;
+
+ ndk::ScopedAStatus getProcessorId(std::string* processorId) override;
ndk::ScopedAStatus getWakeupServiceName(std::string* wakeupServiceName) override;
@@ -95,7 +97,10 @@
bool mTaskWaitStopped GUARDED_BY(mLock);
// A mutex to make sure startTaskLoop does not overlap with stopTaskLoop.
std::mutex mStartStopTaskLoopLock;
- bool mTaskLoopRunning GUARDED_BY(mStartStopTaskLoopLock);
+ bool mTaskLoopRunning GUARDED_BY(mStartStopTaskLoopLock) = false;
+ bool mGrpcConnected GUARDED_BY(mLock) = false;
+ std::unordered_map<std::string, size_t> mClientIdToTaskCount GUARDED_BY(mLock);
+
// Default wait time before retry connecting to remote access client is 10s.
size_t mRetryWaitInMs = 10'000;
std::shared_ptr<DebugRemoteTaskCallback> mDebugCallback;
@@ -103,11 +108,17 @@
void runTaskLoop();
void maybeStartTaskLoop();
void maybeStopTaskLoop();
- ndk::ScopedAStatus getDeviceIdWithClient(
- android::frameworks::automotive::vhal::IVhalClient& client, std::string* deviceId);
+ ndk::ScopedAStatus getVehicleIdWithClient(
+ android::frameworks::automotive::vhal::IVhalClient& client, std::string* vehicleId);
void setRetryWaitInMs(size_t retryWaitInMs) { mRetryWaitInMs = retryWaitInMs; }
void dumpHelp(int fd);
+ void printCurrentStatus(int fd);
+ std::string clientIdToTaskCountToStringLocked() REQUIRES(mLock);
+ void debugInjectTask(int fd, std::string_view clientId, std::string_view taskData);
+ void updateGrpcConnected(bool connected);
+ android::base::Result<void> deliverRemoteTaskThroughCallback(const std::string& clientId,
+ std::string_view taskData);
};
} // namespace remoteaccess
diff --git a/automotive/remoteaccess/hal/default/remoteaccess-tcu-test-service.rc b/automotive/remoteaccess/hal/default/remoteaccess-tcu-test-service.rc
index 6437d70..59315eb 100644
--- a/automotive/remoteaccess/hal/default/remoteaccess-tcu-test-service.rc
+++ b/automotive/remoteaccess/hal/default/remoteaccess-tcu-test-service.rc
@@ -2,3 +2,4 @@
class hal
user vehicle_network
group system inet
+ capabilities NET_RAW
diff --git a/automotive/remoteaccess/hal/default/src/RemoteAccessService.cpp b/automotive/remoteaccess/hal/default/src/RemoteAccessService.cpp
index 4be30a2..bbda9df 100644
--- a/automotive/remoteaccess/hal/default/src/RemoteAccessService.cpp
+++ b/automotive/remoteaccess/hal/default/src/RemoteAccessService.cpp
@@ -36,6 +36,8 @@
using ::aidl::android::hardware::automotive::remoteaccess::ApState;
using ::aidl::android::hardware::automotive::remoteaccess::IRemoteTaskCallback;
using ::aidl::android::hardware::automotive::vehicle::VehicleProperty;
+using ::android::base::Error;
+using ::android::base::Result;
using ::android::base::ScopedLockAssertion;
using ::android::base::StringAppendF;
using ::android::base::StringPrintf;
@@ -48,13 +50,16 @@
using ::ndk::ScopedAStatus;
const std::string WAKEUP_SERVICE_NAME = "com.google.vehicle.wakeup";
+const std::string PROCESSOR_ID = "application_processor";
constexpr char COMMAND_SET_AP_STATE[] = "--set-ap-state";
constexpr char COMMAND_START_DEBUG_CALLBACK[] = "--start-debug-callback";
constexpr char COMMAND_STOP_DEBUG_CALLBACK[] = "--stop-debug-callback";
constexpr char COMMAND_SHOW_TASK[] = "--show-task";
-constexpr char COMMAND_GET_DEVICE_ID[] = "--get-device-id";
+constexpr char COMMAND_GET_VEHICLE_ID[] = "--get-vehicle-id";
+constexpr char COMMAND_INJECT_TASK[] = "--inject-task";
+constexpr char COMMAND_STATUS[] = "--status";
-std::vector<uint8_t> stringToBytes(const std::string& s) {
+std::vector<uint8_t> stringToBytes(std::string_view s) {
const char* data = s.data();
return std::vector<uint8_t>(data, data + s.size());
}
@@ -80,6 +85,10 @@
dprintf(fd, "%s, code: %d, error: %s\n", detail, status.getStatus(), status.getMessage());
}
+std::string boolToString(bool x) {
+ return x ? "true" : "false";
+}
+
} // namespace
RemoteAccessService::RemoteAccessService(WakeupClient::StubInterface* grpcStub)
@@ -125,6 +134,33 @@
mTaskLoopRunning = false;
}
+void RemoteAccessService::updateGrpcConnected(bool connected) {
+ std::lock_guard<std::mutex> lockGuard(mLock);
+ mGrpcConnected = connected;
+}
+
+Result<void> RemoteAccessService::deliverRemoteTaskThroughCallback(const std::string& clientId,
+ std::string_view taskData) {
+ std::shared_ptr<IRemoteTaskCallback> callback;
+ {
+ std::lock_guard<std::mutex> lockGuard(mLock);
+ callback = mRemoteTaskCallback;
+ mClientIdToTaskCount[clientId] += 1;
+ }
+ if (callback == nullptr) {
+ return Error() << "No callback registered, task ignored";
+ }
+ ALOGD("Calling onRemoteTaskRequested callback for client ID: %s", clientId.c_str());
+ ScopedAStatus callbackStatus =
+ callback->onRemoteTaskRequested(clientId, stringToBytes(taskData));
+ if (!callbackStatus.isOk()) {
+ return Error() << "Failed to call onRemoteTaskRequested callback, status: "
+ << callbackStatus.getStatus()
+ << ", message: " << callbackStatus.getMessage();
+ }
+ return {};
+}
+
void RemoteAccessService::runTaskLoop() {
GetRemoteTasksRequest request = {};
std::unique_ptr<ClientReaderInterface<GetRemoteTasksResponse>> reader;
@@ -134,28 +170,19 @@
mGetRemoteTasksContext.reset(new ClientContext());
reader = mGrpcStub->GetRemoteTasks(mGetRemoteTasksContext.get(), request);
}
+ updateGrpcConnected(true);
GetRemoteTasksResponse response;
while (reader->Read(&response)) {
ALOGI("Receiving one task from remote task client");
- std::shared_ptr<IRemoteTaskCallback> callback;
- {
- std::lock_guard<std::mutex> lockGuard(mLock);
- callback = mRemoteTaskCallback;
- }
- if (callback == nullptr) {
- ALOGD("No callback registered, task ignored");
+ if (auto result =
+ deliverRemoteTaskThroughCallback(response.clientid(), response.data());
+ !result.ok()) {
+ ALOGE("%s", result.error().message().c_str());
continue;
}
- ALOGD("Calling onRemoteTaskRequested callback for client ID: %s",
- response.clientid().c_str());
- ScopedAStatus callbackStatus = callback->onRemoteTaskRequested(
- response.clientid(), stringToBytes(response.data()));
- if (!callbackStatus.isOk()) {
- ALOGE("Failed to call onRemoteTaskRequested callback, status: %d, message: %s",
- callbackStatus.getStatus(), callbackStatus.getMessage());
- }
}
+ updateGrpcConnected(false);
Status status = reader->Finish();
mGetRemoteTasksContext.reset();
@@ -176,23 +203,23 @@
}
}
-ScopedAStatus RemoteAccessService::getDeviceId(std::string* deviceId) {
+ScopedAStatus RemoteAccessService::getVehicleId(std::string* vehicleId) {
#ifndef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
auto vhalClient = IVhalClient::tryCreate();
if (vhalClient == nullptr) {
ALOGE("Failed to connect to VHAL");
return ScopedAStatus::fromServiceSpecificErrorWithMessage(
- /*errorCode=*/0, "Failed to connect to VHAL to get device ID");
+ /*errorCode=*/0, "Failed to connect to VHAL to get vehicle ID");
}
- return getDeviceIdWithClient(*vhalClient.get(), deviceId);
+ return getVehicleIdWithClient(*vhalClient.get(), vehicleId);
#else
// Don't use VHAL client in fuzzing since IPC is not allowed.
return ScopedAStatus::ok();
#endif
}
-ScopedAStatus RemoteAccessService::getDeviceIdWithClient(IVhalClient& vhalClient,
- std::string* deviceId) {
+ScopedAStatus RemoteAccessService::getVehicleIdWithClient(IVhalClient& vhalClient,
+ std::string* vehicleId) {
auto result = vhalClient.getValueSync(
*vhalClient.createHalPropValue(toInt(VehicleProperty::INFO_VIN)));
if (!result.ok()) {
@@ -200,7 +227,12 @@
/*errorCode=*/0,
("failed to get INFO_VIN from VHAL: " + result.error().message()).c_str());
}
- *deviceId = (*result)->getStringValue();
+ *vehicleId = (*result)->getStringValue();
+ return ScopedAStatus::ok();
+}
+
+ScopedAStatus RemoteAccessService::getProcessorId(std::string* processorId) {
+ *processorId = PROCESSOR_ID;
return ScopedAStatus::ok();
}
@@ -246,15 +278,17 @@
}
void RemoteAccessService::dumpHelp(int fd) {
- dprintf(fd, "%s",
- (std::string("RemoteAccess HAL debug interface, Usage: \n") + COMMAND_SET_AP_STATE +
- " [0/1](isReadyForRemoteTask) [0/1](isWakeupRequired) Set the new AP state\n" +
- COMMAND_START_DEBUG_CALLBACK +
- " Start a debug callback that will record the received tasks\n" +
- COMMAND_STOP_DEBUG_CALLBACK + " Stop the debug callback\n" + COMMAND_SHOW_TASK +
- " Show tasks received by debug callback\n" + COMMAND_GET_DEVICE_ID +
- " Get device id\n")
- .c_str());
+ dprintf(fd,
+ "RemoteAccess HAL debug interface, Usage: \n"
+ "%s [0/1](isReadyForRemoteTask) [0/1](isWakeupRequired): Set the new AP state\n"
+ "%s: Start a debug callback that will record the received tasks\n"
+ "%s: Stop the debug callback\n"
+ "%s: Show tasks received by debug callback\n"
+ "%s: Get vehicle id\n"
+ "%s [client_id] [task_data]: Inject a task\n"
+ "%s: Show status\n",
+ COMMAND_SET_AP_STATE, COMMAND_START_DEBUG_CALLBACK, COMMAND_STOP_DEBUG_CALLBACK,
+ COMMAND_SHOW_TASK, COMMAND_GET_VEHICLE_ID, COMMAND_INJECT_TASK, COMMAND_STATUS);
}
binder_status_t RemoteAccessService::dump(int fd, const char** args, uint32_t numArgs) {
@@ -265,6 +299,7 @@
if (numArgs == 0) {
dumpHelp(fd);
+ printCurrentStatus(fd);
return STATUS_OK;
}
@@ -316,14 +351,22 @@
dprintf(fd, "Debug callback is not currently used, use \"%s\" first.\n",
COMMAND_START_DEBUG_CALLBACK);
}
- } else if (!strcmp(args[0], COMMAND_GET_DEVICE_ID)) {
- std::string deviceId;
- auto status = getDeviceId(&deviceId);
+ } else if (!strcmp(args[0], COMMAND_GET_VEHICLE_ID)) {
+ std::string vehicleId;
+ auto status = getVehicleId(&vehicleId);
if (!status.isOk()) {
- dprintErrorStatus(fd, "Failed to get device ID", status);
+ dprintErrorStatus(fd, "Failed to get vehicle ID", status);
} else {
- dprintf(fd, "Device Id: %s\n", deviceId.c_str());
+ dprintf(fd, "Vehicle Id: %s\n", vehicleId.c_str());
}
+ } else if (!strcmp(args[0], COMMAND_INJECT_TASK)) {
+ if (numArgs < 3) {
+ dumpHelp(fd);
+ return STATUS_OK;
+ }
+ debugInjectTask(fd, args[1], args[2]);
+ } else if (!strcmp(args[0], COMMAND_STATUS)) {
+ printCurrentStatus(fd);
} else {
dumpHelp(fd);
}
@@ -331,6 +374,37 @@
return STATUS_OK;
}
+void RemoteAccessService::printCurrentStatus(int fd) {
+ std::lock_guard<std::mutex> lockGuard(mLock);
+ dprintf(fd,
+ "\nRemoteAccess HAL status \n"
+ "Remote task callback registered: %s\n"
+ "Task receiving GRPC connection established: %s\n"
+ "Received task count by clientId: \n%s\n",
+ boolToString(mRemoteTaskCallback.get()).c_str(), boolToString(mGrpcConnected).c_str(),
+ clientIdToTaskCountToStringLocked().c_str());
+}
+
+void RemoteAccessService::debugInjectTask(int fd, std::string_view clientId,
+ std::string_view taskData) {
+ std::string clientIdCopy = std::string(clientId);
+ if (auto result = deliverRemoteTaskThroughCallback(clientIdCopy, taskData); !result.ok()) {
+ dprintf(fd, "Failed to inject task: %s", result.error().message().c_str());
+ return;
+ }
+ dprintf(fd, "Task for client: %s, data: [%s] successfully injected\n", clientId.data(),
+ taskData.data());
+}
+
+std::string RemoteAccessService::clientIdToTaskCountToStringLocked() {
+ // Print the table header
+ std::string output = "| ClientId | Count |\n";
+ for (const auto& [clientId, taskCount] : mClientIdToTaskCount) {
+ output += StringPrintf(" %-9s %-6zu\n", clientId.c_str(), taskCount);
+ }
+ return output;
+}
+
ScopedAStatus DebugRemoteTaskCallback::onRemoteTaskRequested(const std::string& clientId,
const std::vector<uint8_t>& data) {
std::lock_guard<std::mutex> lockGuard(mLock);
diff --git a/automotive/remoteaccess/hal/default/test/RemoteAccessServiceUnitTest.cpp b/automotive/remoteaccess/hal/default/test/RemoteAccessServiceUnitTest.cpp
index 8c4fa08..c5afd63 100644
--- a/automotive/remoteaccess/hal/default/test/RemoteAccessServiceUnitTest.cpp
+++ b/automotive/remoteaccess/hal/default/test/RemoteAccessServiceUnitTest.cpp
@@ -187,8 +187,8 @@
void setRetryWaitInMs(size_t retryWaitInMs) { mService->setRetryWaitInMs(retryWaitInMs); }
- ScopedAStatus getDeviceIdWithClient(IVhalClient& vhalClient, std::string* deviceId) {
- return mService->getDeviceIdWithClient(vhalClient, deviceId);
+ ScopedAStatus getVehicleIdWithClient(IVhalClient& vhalClient, std::string* vehicleId) {
+ return mService->getVehicleIdWithClient(vhalClient, vehicleId);
}
private:
@@ -358,13 +358,13 @@
std::this_thread::sleep_for(std::chrono::milliseconds(150));
}
-TEST_F(RemoteAccessServiceUnitTest, testGetDeviceId) {
- std::string deviceId;
+TEST_F(RemoteAccessServiceUnitTest, testGetVehicleId) {
+ std::string vehicleId;
FakeVhalClient vhalClient;
- ASSERT_TRUE(getDeviceIdWithClient(vhalClient, &deviceId).isOk());
- ASSERT_EQ(deviceId, kTestVin);
+ ASSERT_TRUE(getVehicleIdWithClient(vhalClient, &vehicleId).isOk());
+ ASSERT_EQ(vehicleId, kTestVin);
}
} // namespace remoteaccess
diff --git a/automotive/sv/1.0/default/tests/fuzzer/Android.bp b/automotive/sv/1.0/default/tests/fuzzer/Android.bp
index 394c532..696bfad 100644
--- a/automotive/sv/1.0/default/tests/fuzzer/Android.bp
+++ b/automotive/sv/1.0/default/tests/fuzzer/Android.bp
@@ -47,5 +47,13 @@
"android-media-fuzzing-reports@google.com",
],
componentid: 533764,
+ hotlists: [
+ "4593311",
+ ],
+ description: "The fuzzer targets the APIs of android.hardware.automotive.sv@1.0-service",
+ vector: "local_no_privileges_required",
+ service_privilege: "privileged",
+ users: "multi_user",
+ fuzzed_code_usage: "shipped",
},
}
diff --git a/automotive/vehicle/2.0/default/Android.bp b/automotive/vehicle/2.0/default/Android.bp
index 33e211c..586a98e 100644
--- a/automotive/vehicle/2.0/default/Android.bp
+++ b/automotive/vehicle/2.0/default/Android.bp
@@ -292,5 +292,13 @@
"android-media-fuzzing-reports@google.com",
],
componentid: 533764,
+ hotlists: [
+ "4593311",
+ ],
+ description: "The fuzzer targets the APIs of android.hardware.automotive.vehicle@2.0-manager-lib",
+ vector: "local_no_privileges_required",
+ service_privilege: "privileged",
+ users: "multi_user",
+ fuzzed_code_usage: "shipped",
},
}
diff --git a/automotive/vehicle/2.0/default/impl/vhal_v2_0/tests/DefaultVhalImpl_test.cpp b/automotive/vehicle/2.0/default/impl/vhal_v2_0/tests/DefaultVhalImpl_test.cpp
index b5026a6..edd4484 100644
--- a/automotive/vehicle/2.0/default/impl/vhal_v2_0/tests/DefaultVhalImpl_test.cpp
+++ b/automotive/vehicle/2.0/default/impl/vhal_v2_0/tests/DefaultVhalImpl_test.cpp
@@ -756,8 +756,9 @@
// Clear existing events.
mEventQueue.flush();
std::this_thread::sleep_for(std::chrono::milliseconds(500));
- // There should be no new events generated.
- EXPECT_EQ((size_t)0, mEventQueue.flush().size());
+ // Technically there should be no new events generated, however, there might still be one event
+ // in the queue while we are stopping the generator.
+ EXPECT_LE(mEventQueue.flush().size(), 1u);
}
std::string getTestFilePath(const char* filename) {
diff --git a/automotive/vehicle/2.0/types.hal b/automotive/vehicle/2.0/types.hal
index d75b046..5b0a505 100644
--- a/automotive/vehicle/2.0/types.hal
+++ b/automotive/vehicle/2.0/types.hal
@@ -57,46 +57,25 @@
};
/**
- * Vehicle Areas
+ * List of different supported area types for vehicle properties.
* Used to construct property IDs in the VehicleProperty enum.
*
- * Some properties may be associated with particular vehicle areas. For
- * example, VehicleProperty:DOOR_LOCK property must be associated with
- * particular door, thus this property must be marked with
- * VehicleArea:DOOR flag.
+ * Some properties may be associated with particular areas in the vehicle. For example,
+ * VehicleProperty#DOOR_LOCK property must be associated with a particular door, thus this property
+ * must be of the VehicleArea#DOOR area type.
*
- * Other properties may not be associated with particular vehicle area.
- * These kinds of properties must have VehicleArea:GLOBAL flag.
+ * Other properties may not be associated with a particular area in the vehicle. These kinds of
+ * properties must be of the VehicleArea#GLOBAL area type.
*
- * [Definition] Area: An area represents a unique element of an AreaType.
- * For instance, if AreaType is WINDOW, then an area may be FRONT_WINDSHIELD.
- *
- * [Definition] AreaID: An AreaID is a combination of one or more areas,
- * and is represented using a bitmask of Area enums. Different AreaTypes may
- * not be mixed in a single AreaID. For instance, a window area cannot be
- * combined with a seat area in an AreaID.
- *
- * Rules for mapping a zoned property to AreaIDs:
- * - A property must be mapped to an array of AreaIDs that are impacted when
- * the property value changes.
- * - Each element in the array must represent an AreaID, in which the
- * property value can only be changed together in all the areas within
- * the AreaID and never independently. That is, when the property value
- * changes in one of the areas in an AreaID in the array, then it must
- * automatically change in all other areas in the AreaID.
- * - The property value must be independently controllable in any two
- * different AreaIDs in the array.
- * - An area must only appear once in the array of AreaIDs. That is, an
- * area must only be part of a single AreaID in the array.
- *
- * [Definition] Global Property: A property that applies to the entire car
- * and is not associated with a specific area. For example, FUEL_LEVEL,
- * HVAC_STEERING_WHEEL_HEAT.
- *
- * Rules for mapping a global property to AreaIDs:
- * - A global property must not be mapped to AreaIDs.
-*/
+ * Note: This is not the same as areaId used in VehicleAreaConfig. E.g. for a global property, the
+ * property ID is of the VehicleArea#GLOBAL area type, however, the area ID must be 0.
+ */
+// A better name would be VehicleAreaType
enum VehicleArea : int32_t {
+ /**
+ * A global property is a property that applies to the entire vehicle and is not associated with
+ * a specific area. For example, FUEL_LEVEL, HVAC_STEERING_WHEEL_HEAT are global properties.
+ */
GLOBAL = 0x01000000,
/** WINDOW maps to enum VehicleAreaWindow */
WINDOW = 0x03000000,
diff --git a/automotive/vehicle/aidl/android/hardware/automotive/vehicle/VehicleAreaConfig.aidl b/automotive/vehicle/aidl/android/hardware/automotive/vehicle/VehicleAreaConfig.aidl
index abd9540..20c046d 100644
--- a/automotive/vehicle/aidl/android/hardware/automotive/vehicle/VehicleAreaConfig.aidl
+++ b/automotive/vehicle/aidl/android/hardware/automotive/vehicle/VehicleAreaConfig.aidl
@@ -20,7 +20,7 @@
@JavaDerive(equals=true, toString=true)
parcelable VehicleAreaConfig {
/**
- * Area id is ignored for VehiclePropertyGroup:GLOBAL properties.
+ * Area id is always 0 for VehicleArea#GLOBAL properties.
*/
int areaId;
diff --git a/automotive/vehicle/aidl/android/hardware/automotive/vehicle/VehiclePropConfig.aidl b/automotive/vehicle/aidl/android/hardware/automotive/vehicle/VehiclePropConfig.aidl
index 1b48f0b..61b9369 100644
--- a/automotive/vehicle/aidl/android/hardware/automotive/vehicle/VehiclePropConfig.aidl
+++ b/automotive/vehicle/aidl/android/hardware/automotive/vehicle/VehiclePropConfig.aidl
@@ -37,7 +37,25 @@
VehiclePropertyChangeMode changeMode = VehiclePropertyChangeMode.STATIC;
/**
- * Contains per-area configuration.
+ * Contains per-areaId configuration.
+ *
+ * [Definition] area: An area represents a unique element of a VehicleArea. For instance, if the
+ * VehicleArea is WINDOW, then an example area is FRONT_WINDSHIELD.
+ *
+ * [Definition] area ID: An area ID is a combination of one or more areas, and is created by
+ * bitwise "OR"ing the areas together. Areas from different VehicleArea values may not be
+ * mixed in a single area ID. For example, a VehicleAreaWindow area cannot be combined with a
+ * VehicleAreaSeat area in an area ID.
+ *
+ * For VehicleArea#GLOBAL properties, they must map only to a single area ID of 0.
+ *
+ * Rules for mapping a non VehicleArea#GLOBAL property to area IDs:
+ * - A property must be mapped to a set of area IDs that are impacted when the property value
+ * changes.
+ * - An area cannot be part of multiple area IDs, it must only be part of a single area ID.
+ * - When the property value changes in one of the areas in an area ID, then it must
+ * automatically change in all other areas in the area ID.
+ * - The property value must be independently controllable in any two different area IDs.
*/
VehicleAreaConfig[] areaConfigs;
diff --git a/automotive/vehicle/aidl/impl/default_config/config/DefaultProperties.json b/automotive/vehicle/aidl/impl/default_config/config/DefaultProperties.json
index 28790e2..ae36290 100644
--- a/automotive/vehicle/aidl/impl/default_config/config/DefaultProperties.json
+++ b/automotive/vehicle/aidl/impl/default_config/config/DefaultProperties.json
@@ -2371,7 +2371,7 @@
66.19999694824219,
"VehicleUnit::FAHRENHEIT",
19.0,
- 66.5
+ 66.0
]
}
},
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 6fd2367..7b74092 100644
--- a/automotive/vehicle/aidl/impl/fake_impl/hardware/include/FakeVehicleHardware.h
+++ b/automotive/vehicle/aidl/impl/fake_impl/hardware/include/FakeVehicleHardware.h
@@ -178,6 +178,13 @@
void eventFromVehicleBus(
const aidl::android::hardware::automotive::vehicle::VehiclePropValue& value);
+ int getHvacTempNumIncrements(int requestedTemp, int minTemp, int maxTemp, int increment);
+ void updateHvacTemperatureValueSuggestionInput(
+ const std::vector<int>& hvacTemperatureSetConfigArray,
+ std::vector<float>* hvacTemperatureValueSuggestionInput);
+ VhalResult<void> setHvacTemperatureValueSuggestion(
+ const aidl::android::hardware::automotive::vehicle::VehiclePropValue&
+ hvacTemperatureValueSuggestion);
VhalResult<void> maybeSetSpecialValue(
const aidl::android::hardware::automotive::vehicle::VehiclePropValue& value,
bool* isSpecialValue);
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 78c21e9..4544389 100644
--- a/automotive/vehicle/aidl/impl/fake_impl/hardware/src/FakeVehicleHardware.cpp
+++ b/automotive/vehicle/aidl/impl/fake_impl/hardware/src/FakeVehicleHardware.cpp
@@ -67,6 +67,7 @@
using ::aidl::android::hardware::automotive::vehicle::VehiclePropertyStatus;
using ::aidl::android::hardware::automotive::vehicle::VehiclePropertyType;
using ::aidl::android::hardware::automotive::vehicle::VehiclePropValue;
+using ::aidl::android::hardware::automotive::vehicle::VehicleUnit;
using ::android::base::EqualsIgnoreCase;
using ::android::base::Error;
@@ -298,6 +299,94 @@
return {};
}
+int FakeVehicleHardware::getHvacTempNumIncrements(int requestedTemp, int minTemp, int maxTemp,
+ int increment) {
+ requestedTemp = std::max(requestedTemp, minTemp);
+ requestedTemp = std::min(requestedTemp, maxTemp);
+ int numIncrements = (requestedTemp - minTemp) / increment;
+ return numIncrements;
+}
+
+void FakeVehicleHardware::updateHvacTemperatureValueSuggestionInput(
+ const std::vector<int>& hvacTemperatureSetConfigArray,
+ std::vector<float>* hvacTemperatureValueSuggestionInput) {
+ int minTempInCelsius = hvacTemperatureSetConfigArray[0];
+ int maxTempInCelsius = hvacTemperatureSetConfigArray[1];
+ int incrementInCelsius = hvacTemperatureSetConfigArray[2];
+
+ int minTempInFahrenheit = hvacTemperatureSetConfigArray[3];
+ int maxTempInFahrenheit = hvacTemperatureSetConfigArray[4];
+ int incrementInFahrenheit = hvacTemperatureSetConfigArray[5];
+
+ // The HVAC_TEMPERATURE_SET config array values are temperature values that have been multiplied
+ // by 10 and converted to integers. Therefore, requestedTemp must also be multiplied by 10 and
+ // converted to an integer in order for them to be the same units.
+ int requestedTemp = static_cast<int>((*hvacTemperatureValueSuggestionInput)[0] * 10.0f);
+ int numIncrements =
+ (*hvacTemperatureValueSuggestionInput)[1] == toInt(VehicleUnit::CELSIUS)
+ ? getHvacTempNumIncrements(requestedTemp, minTempInCelsius, maxTempInCelsius,
+ incrementInCelsius)
+ : getHvacTempNumIncrements(requestedTemp, minTempInFahrenheit,
+ maxTempInFahrenheit, incrementInFahrenheit);
+
+ int suggestedTempInCelsius = minTempInCelsius + incrementInCelsius * numIncrements;
+ int suggestedTempInFahrenheit = minTempInFahrenheit + incrementInFahrenheit * numIncrements;
+ // HVAC_TEMPERATURE_VALUE_SUGGESTION specifies the temperature values to be in the original
+ // floating point form so we divide by 10 and convert to float.
+ (*hvacTemperatureValueSuggestionInput)[2] = static_cast<float>(suggestedTempInCelsius) / 10.0f;
+ (*hvacTemperatureValueSuggestionInput)[3] =
+ static_cast<float>(suggestedTempInFahrenheit) / 10.0f;
+}
+
+VhalResult<void> FakeVehicleHardware::setHvacTemperatureValueSuggestion(
+ const VehiclePropValue& hvacTemperatureValueSuggestion) {
+ auto hvacTemperatureSetConfigResult =
+ mServerSidePropStore->getConfig(toInt(VehicleProperty::HVAC_TEMPERATURE_SET));
+
+ if (!hvacTemperatureSetConfigResult.ok()) {
+ return StatusError(getErrorCode(hvacTemperatureSetConfigResult)) << StringPrintf(
+ "Failed to set HVAC_TEMPERATURE_VALUE_SUGGESTION because"
+ " HVAC_TEMPERATURE_SET could not be retrieved. Error: %s",
+ getErrorMsg(hvacTemperatureSetConfigResult).c_str());
+ }
+
+ const auto& originalInput = hvacTemperatureValueSuggestion.value.floatValues;
+ if (originalInput.size() != 4) {
+ return StatusError(StatusCode::INVALID_ARG) << StringPrintf(
+ "Failed to set HVAC_TEMPERATURE_VALUE_SUGGESTION because float"
+ " array value is not size 4.");
+ }
+
+ bool isTemperatureUnitSpecified = originalInput[1] == toInt(VehicleUnit::CELSIUS) ||
+ originalInput[1] == toInt(VehicleUnit::FAHRENHEIT);
+ if (!isTemperatureUnitSpecified) {
+ return StatusError(StatusCode::INVALID_ARG) << StringPrintf(
+ "Failed to set HVAC_TEMPERATURE_VALUE_SUGGESTION because float"
+ " value at index 1 is not any of %d or %d, which corresponds to"
+ " VehicleUnit#CELSIUS and VehicleUnit#FAHRENHEIT respectively.",
+ toInt(VehicleUnit::CELSIUS), toInt(VehicleUnit::FAHRENHEIT));
+ }
+
+ auto updatedValue = mValuePool->obtain(hvacTemperatureValueSuggestion);
+ const auto& hvacTemperatureSetConfigArray = hvacTemperatureSetConfigResult.value()->configArray;
+ auto& hvacTemperatureValueSuggestionInput = updatedValue->value.floatValues;
+
+ updateHvacTemperatureValueSuggestionInput(hvacTemperatureSetConfigArray,
+ &hvacTemperatureValueSuggestionInput);
+
+ updatedValue->timestamp = elapsedRealtimeNano();
+ auto writeResult = mServerSidePropStore->writeValue(std::move(updatedValue),
+ /* updateStatus = */ true,
+ VehiclePropertyStore::EventMode::ALWAYS);
+ if (!writeResult.ok()) {
+ return StatusError(getErrorCode(writeResult))
+ << StringPrintf("failed to write value into property store, error: %s",
+ getErrorMsg(writeResult).c_str());
+ }
+
+ return {};
+}
+
bool FakeVehicleHardware::isHvacPropAndHvacNotAvailable(int32_t propId, int32_t areaId) const {
std::unordered_set<int32_t> powerProps(std::begin(HVAC_POWER_PROPERTIES),
std::end(HVAC_POWER_PROPERTIES));
@@ -491,6 +580,9 @@
case VENDOR_PROPERTY_ID:
*isSpecialValue = true;
return StatusError((StatusCode)VENDOR_ERROR_CODE);
+ case toInt(VehicleProperty::HVAC_TEMPERATURE_VALUE_SUGGESTION):
+ *isSpecialValue = true;
+ return setHvacTemperatureValueSuggestion(value);
#ifdef ENABLE_VEHICLE_HAL_TEST_PROPERTIES
case toInt(VehicleProperty::CLUSTER_REPORT_STATE):
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 93a63ad..a50b4ad 100644
--- a/automotive/vehicle/aidl/impl/fake_impl/hardware/test/FakeVehicleHardwareTest.cpp
+++ b/automotive/vehicle/aidl/impl/fake_impl/hardware/test/FakeVehicleHardwareTest.cpp
@@ -57,6 +57,7 @@
using ::aidl::android::hardware::automotive::vehicle::VehicleProperty;
using ::aidl::android::hardware::automotive::vehicle::VehiclePropertyStatus;
using ::aidl::android::hardware::automotive::vehicle::VehiclePropValue;
+using ::aidl::android::hardware::automotive::vehicle::VehicleUnit;
using ::android::base::expected;
using ::android::base::ScopedLockAssertion;
using ::android::base::StringPrintf;
@@ -2242,6 +2243,359 @@
}
}
+TEST_F(FakeVehicleHardwareTest, testSetHvacTemperatureValueSuggestion) {
+ float CELSIUS = static_cast<float>(toInt(VehicleUnit::CELSIUS));
+ float FAHRENHEIT = static_cast<float>(toInt(VehicleUnit::FAHRENHEIT));
+
+ VehiclePropValue floatArraySizeFour = {
+ .prop = toInt(VehicleProperty::HVAC_TEMPERATURE_VALUE_SUGGESTION),
+ .areaId = HVAC_ALL,
+ .value.floatValues = {0, CELSIUS, 0, 0},
+ };
+ StatusCode status = setValue(floatArraySizeFour);
+ EXPECT_EQ(status, StatusCode::OK);
+
+ VehiclePropValue floatArraySizeZero = {
+ .prop = toInt(VehicleProperty::HVAC_TEMPERATURE_VALUE_SUGGESTION),
+ .areaId = HVAC_ALL,
+ };
+ status = setValue(floatArraySizeZero);
+ EXPECT_EQ(status, StatusCode::INVALID_ARG);
+
+ VehiclePropValue floatArraySizeFive = {
+ .prop = toInt(VehicleProperty::HVAC_TEMPERATURE_VALUE_SUGGESTION),
+ .areaId = HVAC_ALL,
+ .value.floatValues = {0, CELSIUS, 0, 0, 0},
+ };
+ status = setValue(floatArraySizeFive);
+ EXPECT_EQ(status, StatusCode::INVALID_ARG);
+
+ VehiclePropValue invalidUnit = {
+ .prop = toInt(VehicleProperty::HVAC_TEMPERATURE_VALUE_SUGGESTION),
+ .areaId = HVAC_ALL,
+ .value.floatValues = {0, 0, 0, 0},
+ };
+ status = setValue(floatArraySizeFive);
+ EXPECT_EQ(status, StatusCode::INVALID_ARG);
+ clearChangedProperties();
+
+ // Config array values from HVAC_TEMPERATURE_SET in DefaultProperties.json
+ auto configs = getHardware()->getAllPropertyConfigs();
+ VehiclePropConfig* hvacTemperatureSetConfig = nullptr;
+ for (auto& config : configs) {
+ if (config.prop == toInt(VehicleProperty::HVAC_TEMPERATURE_SET)) {
+ hvacTemperatureSetConfig = &config;
+ break;
+ }
+ }
+ EXPECT_NE(hvacTemperatureSetConfig, nullptr);
+
+ auto& hvacTemperatureSetConfigArray = hvacTemperatureSetConfig->configArray;
+ // The HVAC_TEMPERATURE_SET config array values are temperature values that have been multiplied
+ // by 10 and converted to integers. HVAC_TEMPERATURE_VALUE_SUGGESTION specifies the temperature
+ // values to be in the original floating point form so we divide by 10 and convert to float.
+ float minTempInCelsius = hvacTemperatureSetConfigArray[0] / 10.0f;
+ float maxTempInCelsius = hvacTemperatureSetConfigArray[1] / 10.0f;
+ float incrementInCelsius = hvacTemperatureSetConfigArray[2] / 10.0f;
+ float minTempInFahrenheit = hvacTemperatureSetConfigArray[3] / 10.0f;
+ float maxTempInFahrenheit = hvacTemperatureSetConfigArray[4] / 10.0f;
+ float incrementInFahrenheit = hvacTemperatureSetConfigArray[5] / 10.0f;
+
+ auto testCases = {
+ SetSpecialValueTestCase{
+ .name = "min_celsius_temperature",
+ .valuesToSet =
+ {
+ VehiclePropValue{
+ .prop = toInt(
+ VehicleProperty::
+ HVAC_TEMPERATURE_VALUE_SUGGESTION),
+ .areaId = HVAC_ALL,
+ .value.floatValues = {minTempInCelsius, CELSIUS, 0, 0},
+ },
+ },
+ .expectedValuesToGet =
+ {
+ VehiclePropValue{
+ .prop = toInt(
+ VehicleProperty::
+ HVAC_TEMPERATURE_VALUE_SUGGESTION),
+ .areaId = HVAC_ALL,
+ .value.floatValues = {minTempInCelsius, CELSIUS,
+ minTempInCelsius,
+ minTempInFahrenheit},
+ },
+ },
+ },
+ SetSpecialValueTestCase{
+ .name = "min_fahrenheit_temperature",
+ .valuesToSet =
+ {
+ VehiclePropValue{
+ .prop = toInt(
+ VehicleProperty::
+ HVAC_TEMPERATURE_VALUE_SUGGESTION),
+ .areaId = HVAC_ALL,
+ .value.floatValues = {minTempInFahrenheit, FAHRENHEIT,
+ 0, 0},
+ },
+ },
+ .expectedValuesToGet =
+ {
+ VehiclePropValue{
+ .prop = toInt(
+ VehicleProperty::
+ HVAC_TEMPERATURE_VALUE_SUGGESTION),
+ .areaId = HVAC_ALL,
+ .value.floatValues = {minTempInFahrenheit, FAHRENHEIT,
+ minTempInCelsius,
+ minTempInFahrenheit},
+ },
+ },
+ },
+ SetSpecialValueTestCase{
+ .name = "max_celsius_temperature",
+ .valuesToSet =
+ {
+ VehiclePropValue{
+ .prop = toInt(
+ VehicleProperty::
+ HVAC_TEMPERATURE_VALUE_SUGGESTION),
+ .areaId = HVAC_ALL,
+ .value.floatValues = {maxTempInCelsius, CELSIUS, 0, 0},
+ },
+ },
+ .expectedValuesToGet =
+ {
+ VehiclePropValue{
+ .prop = toInt(
+ VehicleProperty::
+ HVAC_TEMPERATURE_VALUE_SUGGESTION),
+ .areaId = HVAC_ALL,
+ .value.floatValues = {maxTempInCelsius, CELSIUS,
+ maxTempInCelsius,
+ maxTempInFahrenheit},
+ },
+ },
+ },
+ SetSpecialValueTestCase{
+ .name = "max_fahrenheit_temperature",
+ .valuesToSet =
+ {
+ VehiclePropValue{
+ .prop = toInt(
+ VehicleProperty::
+ HVAC_TEMPERATURE_VALUE_SUGGESTION),
+ .areaId = HVAC_ALL,
+ .value.floatValues = {maxTempInFahrenheit, FAHRENHEIT,
+ 0, 0},
+ },
+ },
+ .expectedValuesToGet =
+ {
+ VehiclePropValue{
+ .prop = toInt(
+ VehicleProperty::
+ HVAC_TEMPERATURE_VALUE_SUGGESTION),
+ .areaId = HVAC_ALL,
+ .value.floatValues = {maxTempInFahrenheit, FAHRENHEIT,
+ maxTempInCelsius,
+ maxTempInFahrenheit},
+ },
+ },
+ },
+ SetSpecialValueTestCase{
+ .name = "below_min_celsius_temperature",
+ .valuesToSet =
+ {
+ VehiclePropValue{
+ .prop = toInt(
+ VehicleProperty::
+ HVAC_TEMPERATURE_VALUE_SUGGESTION),
+ .areaId = HVAC_ALL,
+ .value.floatValues = {minTempInCelsius - 1, CELSIUS, 0,
+ 0},
+ },
+ },
+ .expectedValuesToGet =
+ {
+ VehiclePropValue{
+ .prop = toInt(
+ VehicleProperty::
+ HVAC_TEMPERATURE_VALUE_SUGGESTION),
+ .areaId = HVAC_ALL,
+ .value.floatValues = {minTempInCelsius - 1, CELSIUS,
+ minTempInCelsius,
+ minTempInFahrenheit},
+ },
+ },
+ },
+ SetSpecialValueTestCase{
+ .name = "below_min_fahrenheit_temperature",
+ .valuesToSet =
+ {
+ VehiclePropValue{
+ .prop = toInt(
+ VehicleProperty::
+ HVAC_TEMPERATURE_VALUE_SUGGESTION),
+ .areaId = HVAC_ALL,
+ .value.floatValues = {minTempInFahrenheit - 1,
+ FAHRENHEIT, 0, 0},
+ },
+ },
+ .expectedValuesToGet =
+ {
+ VehiclePropValue{
+ .prop = toInt(
+ VehicleProperty::
+ HVAC_TEMPERATURE_VALUE_SUGGESTION),
+ .areaId = HVAC_ALL,
+ .value.floatValues = {minTempInFahrenheit - 1,
+ FAHRENHEIT, minTempInCelsius,
+ minTempInFahrenheit},
+ },
+ },
+ },
+ SetSpecialValueTestCase{
+ .name = "above_max_celsius_temperature",
+ .valuesToSet =
+ {
+ VehiclePropValue{
+ .prop = toInt(
+ VehicleProperty::
+ HVAC_TEMPERATURE_VALUE_SUGGESTION),
+ .areaId = HVAC_ALL,
+ .value.floatValues = {maxTempInCelsius + 1, CELSIUS, 0,
+ 0},
+ },
+ },
+ .expectedValuesToGet =
+ {
+ VehiclePropValue{
+ .prop = toInt(
+ VehicleProperty::
+ HVAC_TEMPERATURE_VALUE_SUGGESTION),
+ .areaId = HVAC_ALL,
+ .value.floatValues = {maxTempInCelsius + 1, CELSIUS,
+ maxTempInCelsius,
+ maxTempInFahrenheit},
+ },
+ },
+ },
+ SetSpecialValueTestCase{
+ .name = "above_max_fahrenheit_temperature",
+ .valuesToSet =
+ {
+ VehiclePropValue{
+ .prop = toInt(
+ VehicleProperty::
+ HVAC_TEMPERATURE_VALUE_SUGGESTION),
+ .areaId = HVAC_ALL,
+ .value.floatValues = {maxTempInFahrenheit + 1,
+ FAHRENHEIT, 0, 0},
+ },
+ },
+ .expectedValuesToGet =
+ {
+ VehiclePropValue{
+ .prop = toInt(
+ VehicleProperty::
+ HVAC_TEMPERATURE_VALUE_SUGGESTION),
+ .areaId = HVAC_ALL,
+ .value.floatValues = {maxTempInFahrenheit + 1,
+ FAHRENHEIT, maxTempInCelsius,
+ maxTempInFahrenheit},
+ },
+ },
+ },
+ SetSpecialValueTestCase{
+ .name = "inbetween_value_celsius",
+ .valuesToSet =
+ {
+ VehiclePropValue{
+ .prop = toInt(
+ VehicleProperty::
+ HVAC_TEMPERATURE_VALUE_SUGGESTION),
+ .areaId = HVAC_ALL,
+ .value.floatValues = {minTempInCelsius +
+ incrementInCelsius * 2.5f,
+ CELSIUS, 0, 0},
+ },
+ },
+ .expectedValuesToGet =
+ {
+ VehiclePropValue{
+ .prop = toInt(
+ VehicleProperty::
+ HVAC_TEMPERATURE_VALUE_SUGGESTION),
+ .areaId = HVAC_ALL,
+ .value.floatValues =
+ {minTempInCelsius + incrementInCelsius * 2.5f,
+ CELSIUS,
+ minTempInCelsius + incrementInCelsius * 2,
+ minTempInFahrenheit +
+ incrementInFahrenheit * 2},
+ },
+ },
+ },
+ SetSpecialValueTestCase{
+ .name = "inbetween_value_fahrenheit",
+ .valuesToSet =
+ {
+ VehiclePropValue{
+ .prop = toInt(
+ VehicleProperty::
+ HVAC_TEMPERATURE_VALUE_SUGGESTION),
+ .areaId = HVAC_ALL,
+ .value.floatValues = {minTempInFahrenheit +
+ incrementInFahrenheit *
+ 2.5f,
+ FAHRENHEIT, 0, 0},
+ },
+ },
+ .expectedValuesToGet =
+ {
+ VehiclePropValue{
+ .prop = toInt(
+ VehicleProperty::
+ HVAC_TEMPERATURE_VALUE_SUGGESTION),
+ .areaId = HVAC_ALL,
+ .value.floatValues =
+ {minTempInFahrenheit +
+ incrementInFahrenheit * 2.5f,
+ FAHRENHEIT,
+ minTempInCelsius + incrementInCelsius * 2,
+ minTempInFahrenheit +
+ incrementInFahrenheit * 2},
+ },
+ },
+ },
+ };
+
+ for (auto& tc : testCases) {
+ StatusCode status = setValue(tc.valuesToSet[0]);
+ EXPECT_EQ(status, StatusCode::OK);
+
+ auto events = getChangedProperties();
+ EXPECT_EQ(events.size(), static_cast<size_t>(1));
+ events[0].timestamp = 0;
+
+ EXPECT_EQ(events[0], (tc.expectedValuesToGet[0]))
+ << "Failed Test: " << tc.name << "\n"
+ << "Received - prop: " << events[0].prop << ", areaId: " << events[0].areaId
+ << ", floatValues: {" << events[0].value.floatValues[0] << ", "
+ << events[0].value.floatValues[1] << ", " << events[0].value.floatValues[2] << ", "
+ << events[0].value.floatValues[3] << "}\n"
+ << "Expected - prop: " << tc.expectedValuesToGet[0].prop
+ << ", areaId: " << tc.expectedValuesToGet[0].areaId << ", floatValues: {"
+ << tc.expectedValuesToGet[0].value.floatValues[0] << ", "
+ << tc.expectedValuesToGet[0].value.floatValues[1] << ", "
+ << tc.expectedValuesToGet[0].value.floatValues[2] << ", "
+ << tc.expectedValuesToGet[0].value.floatValues[3] << "}\n";
+ clearChangedProperties();
+ }
+}
+
} // namespace fake
} // namespace vehicle
} // namespace automotive
diff --git a/automotive/vehicle/aidl/impl/vhal/vhal-default-service.xml b/automotive/vehicle/aidl/impl/vhal/vhal-default-service.xml
index 4d587ee..9834cdb 100644
--- a/automotive/vehicle/aidl/impl/vhal/vhal-default-service.xml
+++ b/automotive/vehicle/aidl/impl/vhal/vhal-default-service.xml
@@ -1,7 +1,7 @@
<manifest version="1.0" type="device">
<hal format="aidl">
<name>android.hardware.automotive.vehicle</name>
- <version>1</version>
+ <version>2</version>
<fqname>IVehicle/default</fqname>
</hal>
</manifest>
diff --git a/automotive/vehicle/aidl_property/android/hardware/automotive/vehicle/VehicleArea.aidl b/automotive/vehicle/aidl_property/android/hardware/automotive/vehicle/VehicleArea.aidl
index dab0349..6f7f783 100644
--- a/automotive/vehicle/aidl_property/android/hardware/automotive/vehicle/VehicleArea.aidl
+++ b/automotive/vehicle/aidl_property/android/hardware/automotive/vehicle/VehicleArea.aidl
@@ -16,9 +16,28 @@
package android.hardware.automotive.vehicle;
+/**
+ * List of different supported area types for vehicle properties.
+ * Used to construct property IDs in the VehicleProperty enum.
+ *
+ * Some properties may be associated with particular areas in the vehicle. For example,
+ * VehicleProperty#DOOR_LOCK property must be associated with a particular door, thus this property
+ * must be of the VehicleArea#DOOR area type.
+ *
+ * Other properties may not be associated with a particular area in the vehicle. These kinds of
+ * properties must be of the VehicleArea#GLOBAL area type.
+ *
+ * Note: This is not the same as areaId used in VehicleAreaConfig. E.g. for a global property, the
+ * property ID is of the VehicleArea#GLOBAL area type, however, the area ID must be 0.
+ */
@VintfStability
@Backing(type="int")
+// A better name would be VehicleAreaType
enum VehicleArea {
+ /**
+ * A global property is a property that applies to the entire vehicle and is not associated with
+ * a specific area. For example, FUEL_LEVEL, HVAC_STEERING_WHEEL_HEAT are global properties.
+ */
GLOBAL = 0x01000000,
/** WINDOW maps to enum VehicleAreaWindow */
WINDOW = 0x03000000,
diff --git a/automotive/vehicle/aidl_property/android/hardware/automotive/vehicle/VehicleHvacFanDirection.aidl b/automotive/vehicle/aidl_property/android/hardware/automotive/vehicle/VehicleHvacFanDirection.aidl
index fa5d711..f5b77de 100644
--- a/automotive/vehicle/aidl_property/android/hardware/automotive/vehicle/VehicleHvacFanDirection.aidl
+++ b/automotive/vehicle/aidl_property/android/hardware/automotive/vehicle/VehicleHvacFanDirection.aidl
@@ -29,6 +29,9 @@
* FACE_AND_FLOOR = FACE | FLOOR
*/
FACE_AND_FLOOR = 0x3,
+ /**
+ * DEFROST may also be described as the windshield fan direction.
+ */
DEFROST = 0x4,
/**
* DEFROST_AND_FLOOR = DEFROST | FLOOR
diff --git a/automotive/vehicle/aidl_property/android/hardware/automotive/vehicle/VehicleProperty.aidl b/automotive/vehicle/aidl_property/android/hardware/automotive/vehicle/VehicleProperty.aidl
index eeafaaf..8a3ab90 100644
--- a/automotive/vehicle/aidl_property/android/hardware/automotive/vehicle/VehicleProperty.aidl
+++ b/automotive/vehicle/aidl_property/android/hardware/automotive/vehicle/VehicleProperty.aidl
@@ -340,6 +340,9 @@
/**
* Fuel door open
*
+ * This property is defined as VehiclePropertyAccess.READ_WRITE, but OEMs have the option to
+ * implement it as VehiclePropertyAccess.READ only.
+ *
* @change_mode VehiclePropertyChangeMode.ON_CHANGE
* @access VehiclePropertyAccess.READ_WRITE
*/
@@ -375,6 +378,9 @@
/**
* EV charge port open
*
+ * This property is defined as VehiclePropertyAccess.READ_WRITE, but OEMs have the option to
+ * implement it as VehiclePropertyAccess.READ only.
+ *
* @change_mode VehiclePropertyChangeMode.ON_CHANGE
* @access VehiclePropertyAccess.READ_WRITE
*/
@@ -407,9 +413,9 @@
* all energy sources in a vehicle. For example, a hybrid car's range will
* be the sum of the ranges based on fuel and battery.
*
- * This property may be writable because a navigation app could update the range if it has a
- * more accurate estimate based on the upcoming route. However, this property can be set to
- * VehiclePropertyAccess.READ only at the OEM's discretion.
+ * This property is defined as VehiclePropertyAccess.READ_WRITE because a navigation app could
+ * update the range if it has a more accurate estimate based on the upcoming route. However,
+ * this property can be implemented as VehiclePropertyAccess.READ only at the OEM's discretion.
*
* @change_mode VehiclePropertyChangeMode.CONTINUOUS
* @access VehiclePropertyAccess.READ_WRITE
@@ -465,6 +471,9 @@
* If true, the vehicle may automatically shut off the engine when it is not needed and then
* automatically restart it when needed.
*
+ * This property is defined as VehiclePropertyAccess.READ_WRITE, but OEMs have the option to
+ * implement it as VehiclePropertyAccess.READ only.
+ *
* @change_mode VehiclePropertyChangeMode.ON_CHANGE
* @access VehiclePropertyAccess.READ_WRITE
*/
@@ -544,6 +553,9 @@
* regenerated from braking. The minInt32Value in default area's VehicleAreaConfig indicates no
* regenerative braking.
*
+ * This property is defined as VehiclePropertyAccess.READ_WRITE, but OEMs have the option to
+ * implement it as VehiclePropertyAccess.READ only.
+ *
* @change_mode VehiclePropertyChangeMode.ON_CHANGE
* @access VehiclePropertyAccess.READ_WRITE
*/
@@ -629,6 +641,9 @@
*
* The EvStoppingMode enum may be extended to include more states in the future.
*
+ * This property is defined as VehiclePropertyAccess.READ_WRITE, but OEMs have the option to
+ * implement it as VehiclePropertyAccess.READ only.
+ *
* @change_mode VehiclePropertyChangeMode.ON_CHANGE
* @access VehiclePropertyAccess.READ_WRITE
* @data_enum EvStoppingMode
@@ -683,6 +698,9 @@
*
* Fan speed setting
*
+ * This property is defined as VehiclePropertyAccess.READ_WRITE, but OEMs have the option to
+ * implement it as VehiclePropertyAccess.READ only.
+ *
* @change_mode VehiclePropertyChangeMode.ON_CHANGE
* @access VehiclePropertyAccess.READ_WRITE
*/
@@ -691,6 +709,9 @@
/**
* Fan direction setting
*
+ * This property is defined as VehiclePropertyAccess.READ_WRITE, but OEMs have the option to
+ * implement it as VehiclePropertyAccess.READ only.
+ *
* @change_mode VehiclePropertyChangeMode.ON_CHANGE
* @access VehiclePropertyAccess.READ_WRITE
* @data_enum VehicleHvacFanDirection
@@ -727,6 +748,9 @@
* that property to get the suggested value before setting HVAC_TEMPERATURE_SET. Otherwise,
* the application may choose the value in HVAC_TEMPERATURE_SET configArray by itself.
*
+ * This property is defined as VehiclePropertyAccess.READ_WRITE, but OEMs have the option to
+ * implement it as VehiclePropertyAccess.READ only.
+ *
* @change_mode VehiclePropertyChangeMode.ON_CHANGE
* @access VehiclePropertyAccess.READ_WRITE
* @unit VehicleUnit:CELSIUS
@@ -736,6 +760,9 @@
/**
* Fan-based defrost for designated window.
*
+ * This property is defined as VehiclePropertyAccess.READ_WRITE, but OEMs have the option to
+ * implement it as VehiclePropertyAccess.READ only.
+ *
* @change_mode VehiclePropertyChangeMode.ON_CHANGE
* @access VehiclePropertyAccess.READ_WRITE
*/
@@ -744,6 +771,9 @@
/**
* On/off AC for designated areaId
*
+ * This property is defined as VehiclePropertyAccess.READ_WRITE, but OEMs have the option to
+ * implement it as VehiclePropertyAccess.READ only.
+ *
* @change_mode VehiclePropertyChangeMode.ON_CHANGE
* @access VehiclePropertyAccess.READ_WRITE
* @config_flags Supported areaIds
@@ -758,6 +788,9 @@
* Any parameters modified as a side effect of turning on/off the MAX AC
* parameter shall generate onPropertyEvent() callbacks to the VHAL.
*
+ * This property is defined as VehiclePropertyAccess.READ_WRITE, but OEMs have the option to
+ * implement it as VehiclePropertyAccess.READ only.
+ *
* @change_mode VehiclePropertyChangeMode.ON_CHANGE
* @access VehiclePropertyAccess.READ_WRITE
*/
@@ -777,6 +810,9 @@
* areaConfig.areaId = {ROW_1_LEFT | ROW_1_RIGHT} indicates HVAC_MAX_DEFROST_ON
* only can be controlled for the front rows.
*
+ * This property is defined as VehiclePropertyAccess.READ_WRITE, but OEMs have the option to
+ * implement it as VehiclePropertyAccess.READ only.
+ *
* @change_mode VehiclePropertyChangeMode.ON_CHANGE
* @access VehiclePropertyAccess.READ_WRITE
*/
@@ -790,6 +826,9 @@
* Recirc “off” means the majority of the airflow into the cabin is coming
* from outside the car.
*
+ * This property is defined as VehiclePropertyAccess.READ_WRITE, but OEMs have the option to
+ * implement it as VehiclePropertyAccess.READ only.
+ *
* @change_mode VehiclePropertyChangeMode.ON_CHANGE
* @access VehiclePropertyAccess.READ_WRITE
*/
@@ -825,6 +864,9 @@
* onPropertyEvent() callbacks (i.e. HVAC_DUAL_ON = false,
* HVAC_TEMPERATURE_SET[AreaID] = xxx, etc).
*
+ * This property is defined as VehiclePropertyAccess.READ_WRITE, but OEMs have the option to
+ * implement it as VehiclePropertyAccess.READ only.
+ *
* @change_mode VehiclePropertyChangeMode.ON_CHANGE
* @access VehiclePropertyAccess.READ_WRITE
*/
@@ -833,6 +875,9 @@
/**
* On/off automatic mode
*
+ * This property is defined as VehiclePropertyAccess.READ_WRITE, but OEMs have the option to
+ * implement it as VehiclePropertyAccess.READ only.
+ *
* @change_mode VehiclePropertyChangeMode.ON_CHANGE
* @access VehiclePropertyAccess.READ_WRITE
*/
@@ -849,6 +894,9 @@
* min/max range defines the allowable range and number of steps in each
* direction.
*
+ * This property is defined as VehiclePropertyAccess.READ_WRITE, but OEMs have the option to
+ * implement it as VehiclePropertyAccess.READ only.
+ *
* @change_mode VehiclePropertyChangeMode.ON_CHANGE
* @access VehiclePropertyAccess.READ_WRITE
*/
@@ -861,6 +909,9 @@
* The Max value in the config data represents the highest heating level.
* The Min value in the config data MUST be zero and indicates no heating.
*
+ * This property is defined as VehiclePropertyAccess.READ_WRITE, but OEMs have the option to
+ * implement it as VehiclePropertyAccess.READ only.
+ *
* @change_mode VehiclePropertyChangeMode.ON_CHANGE
* @access VehiclePropertyAccess.READ_WRITE
*/
@@ -875,6 +926,9 @@
* Negative value indicates cooling.
* 0 indicates temperature control is off.
*
+ * This property is defined as VehiclePropertyAccess.READ_WRITE, but OEMs have the option to
+ * implement it as VehiclePropertyAccess.READ only.
+ *
* @change_mode VehiclePropertyChangeMode.ON_CHANGE
* @access VehiclePropertyAccess.READ_WRITE
*/
@@ -893,6 +947,12 @@
* Values must be one of VehicleUnit::CELSIUS or VehicleUnit::FAHRENHEIT
* Note that internally, all temperatures are represented in floating point Celsius.
*
+ * If updating HVAC_TEMPERATURE_DISPLAY_UNITS affects the values of other *_DISPLAY_UNITS
+ * properties, then their values must be updated and communicated to the AAOS framework as well.
+ *
+ * This property is defined as VehiclePropertyAccess.READ_WRITE, but OEMs have the option to
+ * implement it as VehiclePropertyAccess.READ only.
+ *
* @change_mode VehiclePropertyChangeMode.ON_CHANGE
* @access VehiclePropertyAccess.READ_WRITE
* @data_enum VehicleUnit
@@ -944,6 +1004,9 @@
* - ROW_1_LEFT | ROW_1_RIGHT
* - ROW_2_LEFT | ROW_2_CENTER | ROW_2_RIGHT | ROW_3_LEFT | ROW_3_CENTER | ROW_3_RIGHT
*
+ * This property is defined as VehiclePropertyAccess.READ_WRITE, but OEMs have the option to
+ * implement it as VehiclePropertyAccess.READ only.
+ *
* @change_mode VehiclePropertyChangeMode.ON_CHANGE
* @access VehiclePropertyAccess.READ_WRITE
*/
@@ -975,6 +1038,9 @@
* switch to recirculation mode if the vehicle detects poor incoming air
* quality.
*
+ * This property is defined as VehiclePropertyAccess.READ_WRITE, but OEMs have the option to
+ * implement it as VehiclePropertyAccess.READ only.
+ *
* @change_mode VehiclePropertyChangeMode.ON_CHANGE
* @access VehiclePropertyAccess.READ_WRITE
*/
@@ -990,6 +1056,9 @@
* ventilation. This is different than seating cooling. It can be on at the
* same time as cooling, or not.
*
+ * This property is defined as VehiclePropertyAccess.READ_WRITE, but OEMs have the option to
+ * implement it as VehiclePropertyAccess.READ only.
+ *
* @change_mode VehiclePropertyChangeMode.ON_CHANGE
* @access VehiclePropertyAccess.READ_WRITE
*/
@@ -998,6 +1067,9 @@
/**
* Electric defrosters' status
*
+ * This property is defined as VehiclePropertyAccess.READ_WRITE, but OEMs have the option to
+ * implement it as VehiclePropertyAccess.READ only.
+ *
* @change_mode VehiclePropertyChangeMode.ON_CHANGE
* @access VehiclePropertyAccess.READ_WRITE
*/
@@ -1054,6 +1126,13 @@
* For example: configArray[0] = METER
* configArray[1] = KILOMETER
* configArray[2] = MILE
+ *
+ * If updating DISTANCE_DISPLAY_UNITS affects the values of other *_DISPLAY_UNITS properties,
+ * then their values must be updated and communicated to the AAOS framework as well.
+ *
+ * This property is defined as VehiclePropertyAccess.READ_WRITE, but OEMs have the option to
+ * implement it as VehiclePropertyAccess.READ only.
+ *
* @change_mode VehiclePropertyChangeMode.ON_CHANGE
* @access VehiclePropertyAccess.READ_WRITE
* @data_enum VehicleUnit
@@ -1070,6 +1149,13 @@
* Volume units are defined in VehicleUnit.
* For example: configArray[0] = LITER
* configArray[1] = GALLON
+ *
+ * If updating FUEL_VOLUME_DISPLAY_UNITS affects the values of other *_DISPLAY_UNITS properties,
+ * then their values must be updated and communicated to the AAOS framework as well.
+ *
+ * This property is defined as VehiclePropertyAccess.READ_WRITE, but OEMs have the option to
+ * implement it as VehiclePropertyAccess.READ only.
+ *
* @change_mode VehiclePropertyChangeMode.ON_CHANGE
* @access VehiclePropertyAccess.READ_WRITE
* @data_enum VehicleUnit
@@ -1087,6 +1173,13 @@
* For example: configArray[0] = KILOPASCAL
* configArray[1] = PSI
* configArray[2] = BAR
+ *
+ * If updating TIRE_PRESSURE_DISPLAY_UNITS affects the values of other *_DISPLAY_UNITS
+ * properties, then their values must be updated and communicated to the AAOS framework as well.
+ *
+ * This property is defined as VehiclePropertyAccess.READ_WRITE, but OEMs have the option to
+ * implement it as VehiclePropertyAccess.READ only.
+ *
* @change_mode VehiclePropertyChangeMode.ON_CHANGE
* @access VehiclePropertyAccess.READ_WRITE
* @data_enum VehicleUnit
@@ -1104,6 +1197,13 @@
* For example: configArray[0] = WATT_HOUR
* configArray[1] = AMPERE_HOURS
* configArray[2] = KILOWATT_HOUR
+ *
+ * If updating EV_BATTERY_DISPLAY_UNITS affects the values of other *_DISPLAY_UNITS properties,
+ * then their values must be updated and communicated to the AAOS framework as well.
+ *
+ * This property is defined as VehiclePropertyAccess.READ_WRITE, but OEMs have the option to
+ * implement it as VehiclePropertyAccess.READ only.
+ *
* @change_mode VehiclePropertyChangeMode.ON_CHANGE
* @access VehiclePropertyAccess.READ_WRITE
* @data_enum VehicleUnit
@@ -1117,6 +1217,9 @@
* True indicates units are distance over volume such as MPG.
* False indicates units are volume over distance such as L/100KM.
*
+ * This property is defined as VehiclePropertyAccess.READ_WRITE, but OEMs have the option to
+ * implement it as VehiclePropertyAccess.READ only.
+ *
* @change_mode VehiclePropertyChangeMode.ON_CHANGE
* @access VehiclePropertyAccess.READ_WRITE
*/
@@ -1132,6 +1235,13 @@
* For example: configArray[0] = METER_PER_SEC
* configArray[1] = MILES_PER_HOUR
* configArray[2] = KILOMETERS_PER_HOUR
+ *
+ * If updating VEHICLE_SPEED_DISPLAY_UNITS affects the values of other *_DISPLAY_UNITS
+ * properties, then their values must be updated and communicated to the AAOS framework as well.
+ *
+ * This property is defined as VehiclePropertyAccess.READ_WRITE, but OEMs have the option to
+ * implement it as VehiclePropertyAccess.READ only.
+ *
* @change_mode VehiclePropertyChangeMode.ON_CHANGE
* @access VehiclePropertyAccess.READ_WRITE
*/
@@ -1259,7 +1369,6 @@
*
* int32Values[0] : VehicleApPowerStateReport enum value
* int32Values[1] : Time in ms to wake up, if necessary. Otherwise 0.
-
*
* @change_mode VehiclePropertyChangeMode.ON_CHANGE
* @access VehiclePropertyAccess.READ_WRITE
@@ -1468,9 +1577,12 @@
* This is an integer in case a door may be set to a particular position.
* Max value indicates fully open, min value (0) indicates fully closed.
*
- * Some vehicles (minivans) can open the door electronically. Hence, the
+ * Some vehicles (minivans) can open the door electronically. Hence, the
* ability to write this property.
*
+ * This property is defined as VehiclePropertyAccess.READ_WRITE, but OEMs have the option to
+ * implement it as VehiclePropertyAccess.READ only.
+ *
* @change_mode VehiclePropertyChangeMode.ON_CHANGE
* @access VehiclePropertyAccess.READ_WRITE
*/
@@ -1479,6 +1591,9 @@
/**
* Door move
*
+ * This property is defined as VehiclePropertyAccess.READ_WRITE, but OEMs have the option to
+ * implement it as VehiclePropertyAccess.READ only.
+ *
* @change_mode VehiclePropertyChangeMode.ON_CHANGE
* @access VehiclePropertyAccess.READ_WRITE
*/
@@ -1489,6 +1604,9 @@
*
* 'true' indicates door is locked
*
+ * This property is defined as VehiclePropertyAccess.READ_WRITE, but OEMs have the option to
+ * implement it as VehiclePropertyAccess.READ only.
+ *
* @change_mode VehiclePropertyChangeMode.ON_CHANGE
* @access VehiclePropertyAccess.READ_WRITE
*/
@@ -1501,6 +1619,9 @@
*
* If enabled, the door is unable to be opened from the inside.
*
+ * This property is defined as VehiclePropertyAccess.READ_WRITE, but OEMs have the option to
+ * implement it as VehiclePropertyAccess.READ only.
+ *
* @change_mode VehiclePropertyChangeMode.ON_CHANGE
* @access VehiclePropertyAccess.READ_WRITE
*/
@@ -1511,6 +1632,9 @@
*
* Positive value indicates tilt upwards, negative value is downwards
*
+ * This property is defined as VehiclePropertyAccess.READ_WRITE, but OEMs have the option to
+ * implement it as VehiclePropertyAccess.READ only.
+ *
* @change_mode VehiclePropertyChangeMode.ON_CHANGE
* @access VehiclePropertyAccess.READ_WRITE
*/
@@ -1521,6 +1645,9 @@
*
* Positive value indicates tilt upwards, negative value is downwards
*
+ * This property is defined as VehiclePropertyAccess.READ_WRITE, but OEMs have the option to
+ * implement it as VehiclePropertyAccess.READ only.
+ *
* @change_mode VehiclePropertyChangeMode.ON_CHANGE
* @access VehiclePropertyAccess.READ_WRITE
*/
@@ -1531,6 +1658,9 @@
*
* Positive value indicate tilt right, negative value is left
*
+ * This property is defined as VehiclePropertyAccess.READ_WRITE, but OEMs have the option to
+ * implement it as VehiclePropertyAccess.READ only.
+ *
* @change_mode VehiclePropertyChangeMode.ON_CHANGE
* @access VehiclePropertyAccess.READ_WRITE
*/
@@ -1541,6 +1671,9 @@
*
* Positive value indicate tilt right, negative value is left
*
+ * This property is defined as VehiclePropertyAccess.READ_WRITE, but OEMs have the option to
+ * implement it as VehiclePropertyAccess.READ only.
+ *
* @change_mode VehiclePropertyChangeMode.ON_CHANGE
* @access VehiclePropertyAccess.READ_WRITE
*/
@@ -1551,6 +1684,9 @@
*
* True indicates mirror positions are locked and not changeable
*
+ * This property is defined as VehiclePropertyAccess.READ_WRITE, but OEMs have the option to
+ * implement it as VehiclePropertyAccess.READ only.
+ *
* @change_mode VehiclePropertyChangeMode.ON_CHANGE
* @access VehiclePropertyAccess.READ_WRITE
*/
@@ -1561,6 +1697,9 @@
*
* True indicates mirrors are folded
*
+ * This property is defined as VehiclePropertyAccess.READ_WRITE, but OEMs have the option to
+ * implement it as VehiclePropertyAccess.READ only.
+ *
* @change_mode VehiclePropertyChangeMode.ON_CHANGE
* @access VehiclePropertyAccess.READ_WRITE
*/
@@ -1574,6 +1713,9 @@
* (for example, when the mirrors fold inward automatically when one exits and locks the
* vehicle) is enabled.
*
+ * This property is defined as VehiclePropertyAccess.READ_WRITE, but OEMs have the option to
+ * implement it as VehiclePropertyAccess.READ only.
+ *
* @change_mode VehiclePropertyChangeMode.ON_CHANGE
* @access VehiclePropertyAccess.READ_WRITE
*/
@@ -1588,6 +1730,9 @@
* (for example, when the mirrors tilt downward automatically when one reverses the vehicle) is
* enabled.
*
+ * This property is defined as VehiclePropertyAccess.READ_WRITE, but OEMs have the option to
+ * implement it as VehiclePropertyAccess.READ only.
+ *
* @change_mode VehiclePropertyChangeMode.ON_CHANGE
* @access VehiclePropertyAccess.READ_WRITE
*/
@@ -1631,6 +1776,9 @@
* Write access indicates automatic seat buckling capabilities. There are
* no known cars at this time, but you never know...
*
+ * This property is defined as VehiclePropertyAccess.READ_WRITE, but OEMs have the option to
+ * implement it as VehiclePropertyAccess.READ only.
+ *
* @change_mode VehiclePropertyChangeMode.ON_CHANGE
* @access VehiclePropertyAccess.READ_WRITE
*/
@@ -1643,6 +1791,9 @@
* Max value indicates highest position
* Min value indicates lowest position
*
+ * This property is defined as VehiclePropertyAccess.READ_WRITE, but OEMs have the option to
+ * implement it as VehiclePropertyAccess.READ only.
+ *
* @change_mode VehiclePropertyChangeMode.ON_CHANGE
* @access VehiclePropertyAccess.READ_WRITE
*/
@@ -1651,6 +1802,9 @@
/**
* Seatbelt height move
*
+ * This property is defined as VehiclePropertyAccess.READ_WRITE, but OEMs have the option to
+ * implement it as VehiclePropertyAccess.READ only.
+ *
* @change_mode VehiclePropertyChangeMode.ON_CHANGE
* @access VehiclePropertyAccess.READ_WRITE
*/
@@ -1663,6 +1817,9 @@
* Max value indicates closest to wheel, min value indicates most rearward
* position.
*
+ * This property is defined as VehiclePropertyAccess.READ_WRITE, but OEMs have the option to
+ * implement it as VehiclePropertyAccess.READ only.
+ *
* @change_mode VehiclePropertyChangeMode.ON_CHANGE
* @access VehiclePropertyAccess.READ_WRITE
*/
@@ -1673,6 +1830,9 @@
*
* Moves the seat position forward and aft.
*
+ * This property is defined as VehiclePropertyAccess.READ_WRITE, but OEMs have the option to
+ * implement it as VehiclePropertyAccess.READ only.
+ *
* @change_mode VehiclePropertyChangeMode.ON_CHANGE
* @access VehiclePropertyAccess.READ_WRITE
*/
@@ -1685,6 +1845,9 @@
* Max value indicates angling forward towards the steering wheel.
* Min value indicates full recline.
*
+ * This property is defined as VehiclePropertyAccess.READ_WRITE, but OEMs have the option to
+ * implement it as VehiclePropertyAccess.READ only.
+ *
* @change_mode VehiclePropertyChangeMode.ON_CHANGE
* @access VehiclePropertyAccess.READ_WRITE
*/
@@ -1695,6 +1858,9 @@
*
* Moves the backrest forward or recline.
*
+ * This property is defined as VehiclePropertyAccess.READ_WRITE, but OEMs have the option to
+ * implement it as VehiclePropertyAccess.READ only.
+ *
* @change_mode VehiclePropertyChangeMode.ON_CHANGE
* @access VehiclePropertyAccess.READ_WRITE
*/
@@ -1707,6 +1873,9 @@
* Max value indicates angling forward towards the steering wheel.
* Min value indicates full recline.
*
+ * This property is defined as VehiclePropertyAccess.READ_WRITE, but OEMs have the option to
+ * implement it as VehiclePropertyAccess.READ only.
+ *
* @change_mode VehiclePropertyChangeMode.ON_CHANGE
* @access VehiclePropertyAccess.READ_WRITE
*/
@@ -1717,6 +1886,9 @@
*
* Moves the backrest forward or recline.
*
+ * This property is defined as VehiclePropertyAccess.READ_WRITE, but OEMs have the option to
+ * implement it as VehiclePropertyAccess.READ only.
+ *
* @change_mode VehiclePropertyChangeMode.ON_CHANGE
* @access VehiclePropertyAccess.READ_WRITE
*/
@@ -1729,6 +1901,9 @@
* Max value indicates highest position.
* Min value indicates lowest position.
*
+ * This property is defined as VehiclePropertyAccess.READ_WRITE, but OEMs have the option to
+ * implement it as VehiclePropertyAccess.READ only.
+ *
* @change_mode VehiclePropertyChangeMode.ON_CHANGE
* @access VehiclePropertyAccess.READ_WRITE
*/
@@ -1739,6 +1914,9 @@
*
* Moves the seat height.
*
+ * This property is defined as VehiclePropertyAccess.READ_WRITE, but OEMs have the option to
+ * implement it as VehiclePropertyAccess.READ only.
+ *
* @change_mode VehiclePropertyChangeMode.ON_CHANGE
* @access VehiclePropertyAccess.READ_WRITE
*/
@@ -1751,6 +1929,9 @@
* Max value indicates longest depth position.
* Min value indicates shortest position.
*
+ * This property is defined as VehiclePropertyAccess.READ_WRITE, but OEMs have the option to
+ * implement it as VehiclePropertyAccess.READ only.
+ *
* @change_mode VehiclePropertyChangeMode.ON_CHANGE
* @access VehiclePropertyAccess.READ_WRITE
*/
@@ -1761,6 +1942,9 @@
*
* Adjusts the seat depth.
*
+ * This property is defined as VehiclePropertyAccess.READ_WRITE, but OEMs have the option to
+ * implement it as VehiclePropertyAccess.READ only.
+ *
* @change_mode VehiclePropertyChangeMode.ON_CHANGE
* @access VehiclePropertyAccess.READ_WRITE
*/
@@ -1773,6 +1957,9 @@
* Max value indicates front edge of seat higher than back edge.
* Min value indicates front edge of seat lower than back edge.
*
+ * This property is defined as VehiclePropertyAccess.READ_WRITE, but OEMs have the option to
+ * implement it as VehiclePropertyAccess.READ only.
+ *
* @change_mode VehiclePropertyChangeMode.ON_CHANGE
* @access VehiclePropertyAccess.READ_WRITE
*/
@@ -1783,6 +1970,9 @@
*
* Tilts the seat.
*
+ * This property is defined as VehiclePropertyAccess.READ_WRITE, but OEMs have the option to
+ * implement it as VehiclePropertyAccess.READ only.
+ *
* @change_mode VehiclePropertyChangeMode.ON_CHANGE
* @access VehiclePropertyAccess.READ_WRITE
*/
@@ -1795,6 +1985,9 @@
* Max value indicates most forward position.
* Min value indicates most rearward position.
*
+ * This property is defined as VehiclePropertyAccess.READ_WRITE, but OEMs have the option to
+ * implement it as VehiclePropertyAccess.READ only.
+ *
* @change_mode VehiclePropertyChangeMode.ON_CHANGE
* @access VehiclePropertyAccess.READ_WRITE
*/
@@ -1805,6 +1998,9 @@
*
* Adjusts the lumbar support.
*
+ * This property is defined as VehiclePropertyAccess.READ_WRITE, but OEMs have the option to
+ * implement it as VehiclePropertyAccess.READ only.
+ *
* @change_mode VehiclePropertyChangeMode.ON_CHANGE
* @access VehiclePropertyAccess.READ_WRITE
*/
@@ -1817,6 +2013,9 @@
* Max value indicates widest lumbar setting (i.e. least support)
* Min value indicates thinnest lumbar setting.
*
+ * This property is defined as VehiclePropertyAccess.READ_WRITE, but OEMs have the option to
+ * implement it as VehiclePropertyAccess.READ only.
+ *
* @change_mode VehiclePropertyChangeMode.ON_CHANGE
* @access VehiclePropertyAccess.READ_WRITE
*/
@@ -1827,6 +2026,9 @@
*
* Adjusts the amount of lateral lumbar support.
*
+ * This property is defined as VehiclePropertyAccess.READ_WRITE, but OEMs have the option to
+ * implement it as VehiclePropertyAccess.READ only.
+ *
* @change_mode VehiclePropertyChangeMode.ON_CHANGE
* @access VehiclePropertyAccess.READ_WRITE
*/
@@ -1843,6 +2045,9 @@
* Max value indicates tallest setting.
* Min value indicates shortest setting.
*
+ * This property is defined as VehiclePropertyAccess.READ_WRITE, but OEMs have the option to
+ * implement it as VehiclePropertyAccess.READ only.
+ *
* @change_mode VehiclePropertyChangeMode.ON_CHANGE
* @access VehiclePropertyAccess.READ_WRITE
*/
@@ -1861,6 +2066,9 @@
*
* This value is not in any particular unit but in a specified range of steps.
*
+ * This property is defined as VehiclePropertyAccess.READ_WRITE, but OEMs have the option to
+ * implement it as VehiclePropertyAccess.READ only.
+ *
* @change_mode VehiclePropertyChangeMode.ON_CHANGE
* @access VehiclePropertyAccess.READ_WRITE
*/
@@ -1872,6 +2080,9 @@
*
* Moves the headrest up and down.
*
+ * This property is defined as VehiclePropertyAccess.READ_WRITE, but OEMs have the option to
+ * implement it as VehiclePropertyAccess.READ only.
+ *
* @change_mode VehiclePropertyChangeMode.ON_CHANGE
* @access VehiclePropertyAccess.READ_WRITE
*/
@@ -1884,6 +2095,9 @@
* Max value indicates most upright angle.
* Min value indicates shallowest headrest angle.
*
+ * This property is defined as VehiclePropertyAccess.READ_WRITE, but OEMs have the option to
+ * implement it as VehiclePropertyAccess.READ only.
+ *
* @change_mode VehiclePropertyChangeMode.ON_CHANGE
* @access VehiclePropertyAccess.READ_WRITE
*/
@@ -1894,6 +2108,9 @@
*
* Adjusts the angle of the headrest
*
+ * This property is defined as VehiclePropertyAccess.READ_WRITE, but OEMs have the option to
+ * implement it as VehiclePropertyAccess.READ only.
+ *
* @change_mode VehiclePropertyChangeMode.ON_CHANGE
* @access VehiclePropertyAccess.READ_WRITE
*/
@@ -1906,6 +2123,9 @@
* Max value indicates position closest to front of car.
* Min value indicates position closest to rear of car.
*
+ * This property is defined as VehiclePropertyAccess.READ_WRITE, but OEMs have the option to
+ * implement it as VehiclePropertyAccess.READ only.
+ *
* @change_mode VehiclePropertyChangeMode.ON_CHANGE
* @access VehiclePropertyAccess.READ_WRITE
*/
@@ -1914,6 +2134,9 @@
/**
* Headrest fore/aft move
*
+ * This property is defined as VehiclePropertyAccess.READ_WRITE, but OEMs have the option to
+ * implement it as VehiclePropertyAccess.READ only.
+ *
* @change_mode VehiclePropertyChangeMode.ON_CHANGE
* @access VehiclePropertyAccess.READ_WRITE
*/
@@ -1955,6 +2178,9 @@
* For each supported area ID, the VehicleAreaConfig#supportedEnumValues must be defined unless
* all enum values of VehicleLightSwitch are supported.
*
+ * This property is defined as VehiclePropertyAccess.READ_WRITE, but OEMs have the option to
+ * implement it as VehiclePropertyAccess.READ only.
+ *
* @change_mode VehiclePropertyChangeMode.ON_CHANGE
* @access VehiclePropertyAccess.READ_WRITE
* @data_enum VehicleLightSwitch
@@ -1968,6 +2194,9 @@
* exit the vehicle. Each area ID must map to the seat that the user is trying to enter/exit
* with the help of the easy access feature.
*
+ * This property is defined as VehiclePropertyAccess.READ_WRITE, but OEMs have the option to
+ * implement it as VehiclePropertyAccess.READ only.
+ *
* @change_mode VehiclePropertyChangeMode.ON_CHANGE
* @access VehiclePropertyAccess.READ_WRITE
*/
@@ -1984,6 +2213,9 @@
* This property can be set to VehiclePropertyAccess.READ read only for the sake of regulation
* or safety concerns.
*
+ * This property is defined as VehiclePropertyAccess.READ_WRITE, but OEMs have the option to
+ * implement it as VehiclePropertyAccess.READ only.
+ *
* @change_mode VehiclePropertyChangeMode.ON_CHANGE
* @access VehiclePropertyAccess.READ_WRITE
*/
@@ -2000,6 +2232,9 @@
*
* This value is not in any particular unit but in a specified range of steps.
*
+ * This property is defined as VehiclePropertyAccess.READ_WRITE, but OEMs have the option to
+ * implement it as VehiclePropertyAccess.READ only.
+ *
* @change_mode VehiclePropertyChangeMode.ON_CHANGE
* @access VehiclePropertyAccess.READ_WRITE
*/
@@ -2019,6 +2254,9 @@
* Larger absolute values, either positive or negative, indicate a faster movement speed. Once
* the seat cushion side support reaches the positional limit, the value resets to 0.
*
+ * This property is defined as VehiclePropertyAccess.READ_WRITE, but OEMs have the option to
+ * implement it as VehiclePropertyAccess.READ only.
+ *
* @change_mode VehiclePropertyChangeMode.ON_CHANGE
* @access VehiclePropertyAccess.READ_WRITE
*/
@@ -2035,6 +2273,9 @@
*
* This value is not in any particular unit but in a specified range of steps.
*
+ * This property is defined as VehiclePropertyAccess.READ_WRITE, but OEMs have the option to
+ * implement it as VehiclePropertyAccess.READ only.
+ *
* @change_mode VehiclePropertyChangeMode.ON_CHANGE
* @access VehiclePropertyAccess.READ_WRITE
*/
@@ -2056,6 +2297,9 @@
*
* This value is not in any particular unit but in a specified range of steps.
*
+ * This property is defined as VehiclePropertyAccess.READ_WRITE, but OEMs have the option to
+ * implement it as VehiclePropertyAccess.READ only.
+ *
* @change_mode VehiclePropertyChangeMode.ON_CHANGE
* @access VehiclePropertyAccess.READ_WRITE
*/
@@ -2077,6 +2321,9 @@
* The area ID must match the seat that actually moves when the walk-in feature activates, not
* the intended seat the passengers will sit in.
*
+ * This property is defined as VehiclePropertyAccess.READ_WRITE, but OEMs have the option to
+ * implement it as VehiclePropertyAccess.READ only.
+ *
* @change_mode VehiclePropertyChangeMode.ON_CHANGE
* @access VehiclePropertyAccess.READ_WRITE
*/
@@ -2108,6 +2355,9 @@
*
* Note that in this mode, 0 indicates the window is closed.
*
+ * This property is defined as VehiclePropertyAccess.READ_WRITE, but OEMs have the option to
+ * implement it as VehiclePropertyAccess.READ only.
+ *
* @change_mode VehiclePropertyChangeMode.ON_CHANGE
* @access VehiclePropertyAccess.READ_WRITE
*/
@@ -2136,6 +2386,9 @@
* Max = open the sunroof, automatically stop when sunroof is fully open.
* Min = open the vent, automatically stop when vent is fully open.
*
+ * This property is defined as VehiclePropertyAccess.READ_WRITE, but OEMs have the option to
+ * implement it as VehiclePropertyAccess.READ only.
+ *
* @change_mode VehiclePropertyChangeMode.ON_CHANGE
* @access VehiclePropertyAccess.READ_WRITE
*/
@@ -2146,6 +2399,9 @@
*
* True indicates windows are locked and can't be moved.
*
+ * This property is defined as VehiclePropertyAccess.READ_WRITE, but OEMs have the option to
+ * implement it as VehiclePropertyAccess.READ only.
+ *
* @change_mode VehiclePropertyChangeMode.ON_CHANGE
* @access VehiclePropertyAccess.READ_WRITE
*/
@@ -2203,12 +2459,12 @@
* unless all states in WindshieldWipersSwitch are supported (including OTHER, which is not
* recommended).
*
- * This property is defined as read_write, but OEMs have the option to implement it as read
- * only.
+ * This property is defined as VehiclePropertyAccess.READ_WRITE, but OEMs have the option to
+ * implement it as VehiclePropertyAccess.READ only.
*
- * If this property is implemented as read_write and the OTHER state is listed in the
- * VehicleAreaConfig#supportedEnumValues array, then OTHER is not a supported value for writing.
- * It is only a supported value for reading.
+ * If this property is implemented as VehiclePropertyAccess.READ_WRITE and the OTHER state is
+ * listed in the VehicleAreaConfig#supportedEnumValues array, then OTHER is not a supported
+ * value for writing. It is only a supported value for reading.
*
* @change_mode VehiclePropertyChangeMode.ON_CHANGE
* @access VehiclePropertyAccess.READ_WRITE
@@ -2231,6 +2487,9 @@
*
* This value is not in any particular unit but in a specified range of steps.
*
+ * This property is defined as VehiclePropertyAccess.READ_WRITE, but OEMs have the option to
+ * implement it as VehiclePropertyAccess.READ only.
+ *
* @change_mode VehiclePropertyChangeMode.ON_CHANGE
* @access VehiclePropertyAccess.READ_WRITE
*/
@@ -2250,6 +2509,9 @@
*
* This value is not in any particular unit but in a specified range of steps.
*
+ * This property is defined as VehiclePropertyAccess.READ_WRITE, but OEMs have the option to
+ * implement it as VehiclePropertyAccess.READ only.
+ *
* @change_mode VehiclePropertyChangeMode.ON_CHANGE
* @access VehiclePropertyAccess.READ_WRITE
*/
@@ -2267,6 +2529,9 @@
*
* This value is not in any particular unit but in a specified range of steps.
*
+ * This property is defined as VehiclePropertyAccess.READ_WRITE, but OEMs have the option to
+ * implement it as VehiclePropertyAccess.READ only.
+ *
* @change_mode VehiclePropertyChangeMode.ON_CHANGE
* @access VehiclePropertyAccess.READ_WRITE
*/
@@ -2285,6 +2550,9 @@
*
* This value is not in any particular unit but in a specified range of steps.
*
+ * This property is defined as VehiclePropertyAccess.READ_WRITE, but OEMs have the option to
+ * implement it as VehiclePropertyAccess.READ only.
+ *
* @change_mode VehiclePropertyChangeMode.ON_CHANGE
* @access VehiclePropertyAccess.READ_WRITE
*/
@@ -2296,6 +2564,9 @@
* If true, the steering wheel will lock automatically to prevent theft in certain
* situations.
*
+ * This property is defined as VehiclePropertyAccess.READ_WRITE, but OEMs have the option to
+ * implement it as VehiclePropertyAccess.READ only.
+ *
* @change_mode VehiclePropertyChangeMode.ON_CHANGE
* @access VehiclePropertyAccess.READ_WRITE
*/
@@ -2306,6 +2577,9 @@
*
* If true, the steering wheel's position is locked and not changeable.
*
+ * This property is defined as VehiclePropertyAccess.READ_WRITE, but OEMs have the option to
+ * implement it as VehiclePropertyAccess.READ only.
+ *
* @change_mode VehiclePropertyChangeMode.ON_CHANGE
* @access VehiclePropertyAccess.READ_WRITE
*/
@@ -2317,6 +2591,9 @@
* If true, the driver’s steering wheel will automatically adjust to make it easier for the
* driver to enter and exit the vehicle.
*
+ * This property is defined as VehiclePropertyAccess.READ_WRITE, but OEMs have the option to
+ * implement it as VehiclePropertyAccess.READ only.
+ *
* @change_mode VehiclePropertyChangeMode.ON_CHANGE
* @access VehiclePropertyAccess.READ_WRITE
*/
@@ -2339,6 +2616,9 @@
* front right dashboard has a glove box embedded in it, then the area ID should be
* SEAT_1_RIGHT).
*
+ * This property is defined as VehiclePropertyAccess.READ_WRITE, but OEMs have the option to
+ * implement it as VehiclePropertyAccess.READ only.
+ *
* @change_mode VehiclePropertyChangeMode.ON_CHANGE
* @access VehiclePropertyAccess.READ_WRITE
*/
@@ -2354,6 +2634,9 @@
* front right dashboard has a glove box embedded in it, then the area ID should be
* VehicleAreaSeat#ROW_1_RIGHT).
*
+ * This property is defined as VehiclePropertyAccess.READ_WRITE, but OEMs have the option to
+ * implement it as VehiclePropertyAccess.READ only.
+ *
* @change_mode VehiclePropertyChangeMode.ON_CHANGE
* @access VehiclePropertyAccess.READ_WRITE
*/
@@ -2385,7 +2668,12 @@
* when computing the vehicle's location that is shared with Android through the GNSS HAL.
*
* The value must return a collection of bit flags. The bit flags are defined in
- * LocationCharacterization.
+ * LocationCharacterization. The value must also include exactly one of DEAD_RECKONED or
+ * RAW_GNSS_ONLY among its collection of bit flags.
+ *
+ * When this property is not supported, it is assumed that no additional sensor inputs are fused
+ * into the GNSS updates provided through the GNSS HAL. That is unless otherwise specified
+ * through the GNSS HAL interfaces.
*
* @change_mode VehiclePropertyChangeMode.STATIC
* @access VehiclePropertyAccess.READ
@@ -2565,6 +2853,9 @@
*
* The setting that the user wants.
*
+ * This property is defined as VehiclePropertyAccess.READ_WRITE, but OEMs have the option to
+ * implement it as VehiclePropertyAccess.READ only.
+ *
* @change_mode VehiclePropertyChangeMode.ON_CHANGE
* @access VehiclePropertyAccess.READ_WRITE
* @data_enum VehicleLightSwitch
@@ -2576,6 +2867,9 @@
*
* The setting that the user wants.
*
+ * This property is defined as VehiclePropertyAccess.READ_WRITE, but OEMs have the option to
+ * implement it as VehiclePropertyAccess.READ only.
+ *
* @change_mode VehiclePropertyChangeMode.ON_CHANGE
* @access VehiclePropertyAccess.READ_WRITE
* @data_enum VehicleLightSwitch
@@ -2603,6 +2897,9 @@
* Only one of FOG_LIGHTS_SWITCH or REAR_FOG_LIGHTS_SWITCH must be implemented and not both.
* FRONT_FOG_LIGHTS_SWITCH must not be implemented.
*
+ * This property is defined as VehiclePropertyAccess.READ_WRITE, but OEMs have the option to
+ * implement it as VehiclePropertyAccess.READ only.
+ *
* @change_mode VehiclePropertyChangeMode.ON_CHANGE
* @access VehiclePropertyAccess.READ_WRITE
* @data_enum VehicleLightSwitch
@@ -2614,6 +2911,9 @@
*
* The setting that the user wants.
*
+ * This property is defined as VehiclePropertyAccess.READ_WRITE, but OEMs have the option to
+ * implement it as VehiclePropertyAccess.READ only.
+ *
* @change_mode VehiclePropertyChangeMode.ON_CHANGE
* @access VehiclePropertyAccess.READ_WRITE
* @data_enum VehicleLightSwitch
@@ -2639,6 +2939,9 @@
* is open or because of a voice command.
* For example, while the switch is in the "off" or "automatic" position.
*
+ * This property is defined as VehiclePropertyAccess.READ_WRITE, but OEMs have the option to
+ * implement it as VehiclePropertyAccess.READ only.
+ *
* @change_mode VehiclePropertyChangeMode.ON_CHANGE
* @access VehiclePropertyAccess.READ_WRITE
* @data_enum VehicleLightSwitch
@@ -2664,6 +2967,9 @@
* is open or because of a voice command.
* For example, while the switch is in the "off" or "automatic" position.
*
+ * This property is defined as VehiclePropertyAccess.READ_WRITE, but OEMs have the option to
+ * implement it as VehiclePropertyAccess.READ only.
+ *
* @change_mode VehiclePropertyChangeMode.ON_CHANGE
* @access VehiclePropertyAccess.READ_WRITE
* @data_enum VehicleLightSwitch
@@ -2706,6 +3012,9 @@
* For the global area ID (0), the VehicleAreaConfig#supportedEnumValues must be defined unless
* all enum values of VehicleLightSwitch are supported.
*
+ * This property is defined as VehiclePropertyAccess.READ_WRITE, but OEMs have the option to
+ * implement it as VehiclePropertyAccess.READ only.
+ *
* @change_mode VehiclePropertyChangeMode.ON_CHANGE
* @access VehiclePropertyAccess.READ_WRITE
* @data_enum VehicleLightSwitch
@@ -3372,6 +3681,9 @@
* Only one of FOG_LIGHTS_SWITCH or FRONT_FOG_LIGHTS_SWITCH must be implemented. Please refer to
* the documentation on FOG_LIGHTS_SWITCH for more information.
*
+ * This property is defined as VehiclePropertyAccess.READ_WRITE, but OEMs have the option to
+ * implement it as VehiclePropertyAccess.READ only.
+ *
* @change_mode VehiclePropertyChangeMode.ON_CHANGE
* @access VehiclePropertyAccess.READ_WRITE
* @data_enum VehicleLightSwitch
@@ -3400,6 +3712,9 @@
* Only one of FOG_LIGHTS_SWITCH or REAR_FOG_LIGHTS_SWITCH must be implemented. Please refer to
* the documentation on FOG_LIGHTS_SWITCH for more information.
*
+ * This property is defined as VehiclePropertyAccess.READ_WRITE, but OEMs have the option to
+ * implement it as VehiclePropertyAccess.READ only.
+ *
* @change_mode VehiclePropertyChangeMode.ON_CHANGE
* @access VehiclePropertyAccess.READ_WRITE
* @data_enum VehicleLightSwitch
@@ -3413,6 +3728,9 @@
* configArray[0] is used to specify the max current draw allowed by
* the vehicle in Amperes.
*
+ * This property is defined as VehiclePropertyAccess.READ_WRITE, but OEMs have the option to
+ * implement it as VehiclePropertyAccess.READ only.
+ *
* @change_mode VehiclePropertyChangeMode.ON_CHANGE
* @access VehiclePropertyAccess.READ_WRITE
* @unit VehicleUnit:AMPERE
@@ -3431,6 +3749,9 @@
* then the configArray should be {20, 40, 60, 80, 100}
* If the configArray is empty then all values from 0 to 100 must be valid.
*
+ * This property is defined as VehiclePropertyAccess.READ_WRITE, but OEMs have the option to
+ * implement it as VehiclePropertyAccess.READ only.
+ *
* @change_mode VehiclePropertyChangeMode.ON_CHANGE
* @access VehiclePropertyAccess.READ_WRITE
*/
@@ -3455,6 +3776,9 @@
* The setting that the user wants. Setting this property to true starts the battery charging
* and setting to false stops charging.
*
+ * This property is defined as VehiclePropertyAccess.READ_WRITE, but OEMs have the option to
+ * implement it as VehiclePropertyAccess.READ only.
+ *
* @change_mode VehiclePropertyChangeMode.ON_CHANGE
* @access VehiclePropertyAccess.READ_WRITE
*/
@@ -3647,8 +3971,8 @@
* low, that information must be conveyed through the ErrorState values in the
* AUTOMATIC_EMERGENCY_BRAKING_STATE property.
*
- * This property is defined as read_write, but OEMs have the option to implement it as read
- * only.
+ * This property is defined as VehiclePropertyAccess.READ_WRITE, but OEMs have the option to
+ * implement it as VehiclePropertyAccess.READ only.
*
* @change_mode VehiclePropertyChangeMode.ON_CHANGE
* @access VehiclePropertyAccess.READ_WRITE
@@ -3689,8 +4013,8 @@
* low, that information must be conveyed through the ErrorState values in the
* FORWARD_COLLISION_WARNING_STATE property.
*
- * This property is defined as read_write, but OEMs have the option to implement it as read
- * only.
+ * This property is defined as VehiclePropertyAccess.READ_WRITE, but OEMs have the option to
+ * implement it as VehiclePropertyAccess.READ only.
*
* @change_mode VehiclePropertyChangeMode.ON_CHANGE
* @access VehiclePropertyAccess.READ_WRITE
@@ -3728,8 +4052,8 @@
* information must be conveyed through the ErrorState values in the BLIND_SPOT_WARNING_STATE
* property.
*
- * This property is defined as read_write, but OEMs have the option to implement it as read
- * only.
+ * This property is defined as VehiclePropertyAccess.READ_WRITE, but OEMs have the option to
+ * implement it as VehiclePropertyAccess.READ only.
*
* @change_mode VehiclePropertyChangeMode.ON_CHANGE
* @access VehiclePropertyAccess.READ_WRITE
@@ -3768,8 +4092,8 @@
* high, that information must be conveyed through the ErrorState values in the
* LANE_DEPARTURE_WARNING_STATE property.
*
- * This property is defined as read_write, but OEMs have the option to implement it as read
- * only.
+ * This property is defined as VehiclePropertyAccess.READ_WRITE, but OEMs have the option to
+ * implement it as VehiclePropertyAccess.READ only.
*
* @change_mode VehiclePropertyChangeMode.ON_CHANGE
* @access VehiclePropertyAccess.READ_WRITE
@@ -3812,8 +4136,8 @@
* high, that information must be conveyed through the ErrorState values in the
* LANE_KEEP_ASSIST_STATE property.
*
- * This property is defined as read_write, but OEMs have the option to implement it as read
- * only.
+ * This property is defined as VehiclePropertyAccess.READ_WRITE, but OEMs have the option to
+ * implement it as VehiclePropertyAccess.READ only.
*
* @change_mode VehiclePropertyChangeMode.ON_CHANGE
* @access VehiclePropertyAccess.READ_WRITE
@@ -3860,8 +4184,8 @@
* high, that information must be conveyed through the ErrorState values in the
* LANE_CENTERING_ASSIST_STATE property.
*
- * This property is defined as read_write, but OEMs have the option to implement it as read
- * only.
+ * This property is defined as VehiclePropertyAccess.READ_WRITE, but OEMs have the option to
+ * implement it as VehiclePropertyAccess.READ only.
*
* @change_mode VehiclePropertyChangeMode.ON_CHANGE
* @access VehiclePropertyAccess.READ_WRITE
@@ -3928,8 +4252,8 @@
* low, that information must be conveyed through the ErrorState values in the
* EMERGENCY_LANE_KEEP_ASSIST_STATE property.
*
- * This property is defined as read_write, but OEMs have the option to implement it as read
- * only.
+ * This property is defined as VehiclePropertyAccess.READ_WRITE, but OEMs have the option to
+ * implement it as VehiclePropertyAccess.READ only.
*
* @change_mode VehiclePropertyChangeMode.ON_CHANGE
* @access VehiclePropertyAccess.READ_WRITE
@@ -3971,8 +4295,8 @@
* information must be conveyed through the ErrorState values in the CRUISE_CONTROL_STATE
* property.
*
- * This property is defined as read_write, but OEMs have the option to implement it as read
- * only.
+ * This property is defined as VehiclePropertyAccess.READ_WRITE, but OEMs have the option to
+ * implement it as VehiclePropertyAccess.READ only.
*
* @change_mode VehiclePropertyChangeMode.ON_CHANGE
* @access VehiclePropertyAccess.READ_WRITE
@@ -3996,6 +4320,9 @@
* Trying to write CruiseControlType#OTHER or an ErrorState to this property will throw an
* IllegalArgumentException.
*
+ * This property is defined as VehiclePropertyAccess.READ_WRITE, but OEMs have the option to
+ * implement it as VehiclePropertyAccess.READ only.
+ *
* @change_mode VehiclePropertyChangeMode.ON_CHANGE
* @access VehiclePropertyAccess.READ_WRITE
* @data_enum CruiseControlType
@@ -4031,7 +4358,10 @@
*
* For the global area ID (0), the VehicleAreaConfig#supportedEnumValues array must be defined
* unless all states of CruiseControlState are supported. Any unsupported commands sent through
- * this property should return StatusCode.INVALID_ARG.
+ * this property must return StatusCode#INVALID_ARG.
+ *
+ * When this property is not available because CC is disabled (i.e. CRUISE_CONTROL_ENABLED is
+ * false), this property must return StatusCode#NOT_AVAILABLE_DISABLED.
*
* @change_mode VehiclePropertyChangeMode.ON_CHANGE
* @access VehiclePropertyAccess.WRITE
@@ -4049,8 +4379,8 @@
* The maxFloatValue represents the upper bound of the target speed.
* The minFloatValue represents the lower bound of the target speed.
*
- * When this property is not available (for example when CRUISE_CONTROL_ENABLED is false), it
- * should return StatusCode.NOT_AVAILABLE_DISABLED.
+ * When this property is not available because CC is disabled (i.e. CRUISE_CONTROL_ENABLED is
+ * false), this property must return StatusCode#NOT_AVAILABLE_DISABLED.
*
* @change_mode VehiclePropertyChangeMode.ON_CHANGE
* @access VehiclePropertyAccess.READ
@@ -4072,7 +4402,11 @@
* ascending order. All values must be positive. If the property is writable, all values must be
* writable.
*
- * Writing to this property when it is not available should return StatusCode.NOT_AVAILABLE.
+ * When this property is not available because CC is disabled (i.e. CRUISE_CONTROL_ENABLED is
+ * false), this property must return StatusCode#NOT_AVAILABLE_DISABLED.
+ *
+ * This property is defined as VehiclePropertyAccess.READ_WRITE, but OEMs have the option to
+ * implement it as VehiclePropertyAccess.READ only.
*
* @change_mode VehiclePropertyChangeMode.ON_CHANGE
* @access VehiclePropertyAccess.READ_WRITE
@@ -4096,6 +4430,9 @@
* vehicle is too far away for the sensor to detect), this property should return
* StatusCode.NOT_AVAILABLE.
*
+ * When this property is not available because CC is disabled (i.e. CRUISE_CONTROL_ENABLED is
+ * false), this property must return StatusCode#NOT_AVAILABLE_DISABLED.
+ *
* @change_mode VehiclePropertyChangeMode.CONTINUOUS
* @access VehiclePropertyAccess.READ
* @unit VehicleUnit:MILLIMETER
@@ -4114,8 +4451,8 @@
* not available due to some temporary state, that information must be conveyed through the
* ErrorState values in the HANDS_ON_DETECTION_STATE property.
*
- * This property is defined as read_write, but OEMs have the option to implement it as read
- * only.
+ * This property is defined as VehiclePropertyAccess.READ_WRITE, but OEMs have the option to
+ * implement it as VehiclePropertyAccess.READ only.
*
* @change_mode VehiclePropertyChangeMode.ON_CHANGE
* @access VehiclePropertyAccess.READ_WRITE
diff --git a/bluetooth/1.0/default/test/fuzzer/Android.bp b/bluetooth/1.0/default/test/fuzzer/Android.bp
index 691136f..de2b46d 100644
--- a/bluetooth/1.0/default/test/fuzzer/Android.bp
+++ b/bluetooth/1.0/default/test/fuzzer/Android.bp
@@ -60,5 +60,13 @@
"android-media-fuzzing-reports@google.com",
],
componentid: 533764,
+ hotlists: [
+ "4593311",
+ ],
+ description: "The fuzzer targets the APIs of android.hardware.bluetooth@1.0-impl library",
+ vector: "local_no_privileges_required",
+ service_privilege: "privileged",
+ users: "multi_user",
+ fuzzed_code_usage: "shipped",
},
}
diff --git a/bluetooth/aidl/TEST_MAPPING b/bluetooth/aidl/TEST_MAPPING
index 342a1e4..f059c04 100644
--- a/bluetooth/aidl/TEST_MAPPING
+++ b/bluetooth/aidl/TEST_MAPPING
@@ -1,7 +1,13 @@
{
"presubmit" : [
{
- "name" : "VtsHalBluetoothTargetTest"
+ "name" : "VtsHalBluetoothTargetTest",
+ "options": [
+ {
+ // TODO(b/275847929)
+ "exclude-filter": "VtsHalBluetoothTargetTest.PerInstance/BluetoothAidlTest#Cdd_C_12_1_Bluetooth5Requirements/0_android_hardware_bluetooth_IBluetoothHci_default"
+ }
+ ]
}
]
}
diff --git a/bluetooth/aidl/vts/Android.bp b/bluetooth/aidl/vts/Android.bp
index 414f707..5fc0b2e 100644
--- a/bluetooth/aidl/vts/Android.bp
+++ b/bluetooth/aidl/vts/Android.bp
@@ -13,7 +13,17 @@
"VtsHalTargetTestDefaults",
"use_libaidlvintf_gtest_helper_static",
],
- srcs: ["VtsHalBluetoothTargetTest.cpp"],
+ srcs: [
+ "VtsHalBluetoothTargetTest.cpp",
+ ":BluetoothPacketSources",
+ ":BluetoothHciPacketSources",
+ ],
+ generated_headers: [
+ "BluetoothGeneratedPackets_h",
+ ],
+ include_dirs: [
+ "packages/modules/Bluetooth/system/gd",
+ ],
shared_libs: [
"libbase",
"libbinder_ndk",
@@ -45,4 +55,8 @@
tidy_flags: [
"--header-filter=^.*tools\\/rootcanal\\/(model|include|net|desktop)\\/(.(?!\\.pb\\.h))*$",
],
+ tidy_disabled_srcs: [
+ ":BluetoothPacketSources",
+ ":BluetoothHciPacketSources",
+ ],
}
diff --git a/bluetooth/aidl/vts/VtsHalBluetoothTargetTest.cpp b/bluetooth/aidl/vts/VtsHalBluetoothTargetTest.cpp
index 3704c3d..529e092 100644
--- a/bluetooth/aidl/vts/VtsHalBluetoothTargetTest.cpp
+++ b/bluetooth/aidl/vts/VtsHalBluetoothTargetTest.cpp
@@ -24,27 +24,50 @@
#include <android/binder_manager.h>
#include <android/binder_process.h>
#include <binder/IServiceManager.h>
-#include <binder/ProcessState.h>
#include <atomic>
#include <chrono>
#include <condition_variable>
#include <future>
-#include <mutex>
#include <queue>
#include <thread>
+#include <utility>
#include <vector>
+// TODO: Remove custom logging defines from PDL packets.
+#undef LOG_INFO
+#undef LOG_DEBUG
+#undef LOG_TAG
+#define LOG_TAG "VtsHalBluetooth"
+#include "hci/hci_packets.h"
+#include "packet/raw_builder.h"
+
using aidl::android::hardware::bluetooth::IBluetoothHci;
using aidl::android::hardware::bluetooth::IBluetoothHciCallbacks;
using aidl::android::hardware::bluetooth::Status;
using ndk::ScopedAStatus;
using ndk::SpAIBinder;
-// Bluetooth Core Specification 3.0 + HS
-static constexpr uint8_t kHciMinimumHciVersion = 5;
-// Bluetooth Core Specification 3.0 + HS
-static constexpr uint8_t kHciMinimumLmpVersion = 5;
+using ::bluetooth::hci::CommandBuilder;
+using ::bluetooth::hci::CommandCompleteView;
+using ::bluetooth::hci::CommandView;
+using ::bluetooth::hci::ErrorCode;
+using ::bluetooth::hci::EventView;
+using ::bluetooth::hci::LeReadLocalSupportedFeaturesBuilder;
+using ::bluetooth::hci::LeReadLocalSupportedFeaturesCompleteView;
+using ::bluetooth::hci::LeReadNumberOfSupportedAdvertisingSetsBuilder;
+using ::bluetooth::hci::LeReadNumberOfSupportedAdvertisingSetsCompleteView;
+using ::bluetooth::hci::LeReadResolvingListSizeBuilder;
+using ::bluetooth::hci::LeReadResolvingListSizeCompleteView;
+using ::bluetooth::hci::LLFeaturesBits;
+using ::bluetooth::hci::OpCode;
+using ::bluetooth::hci::OpCodeText;
+using ::bluetooth::hci::PacketView;
+using ::bluetooth::hci::ReadLocalVersionInformationBuilder;
+using ::bluetooth::hci::ReadLocalVersionInformationCompleteView;
+
+static constexpr uint8_t kMinLeAdvSetForBt5 = 16;
+static constexpr uint8_t kMinLeResolvingListForBt5 = 8;
static constexpr size_t kNumHciCommandsBandwidth = 100;
static constexpr size_t kNumScoPacketsBandwidth = 100;
@@ -55,65 +78,14 @@
static constexpr std::chrono::milliseconds kWaitForAclDataTimeout(1000);
static constexpr std::chrono::milliseconds kInterfaceCloseDelayMs(200);
-static constexpr uint8_t kCommandHciShouldBeUnknown[] = {
- 0xff, 0x3B, 0x08, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07};
-static constexpr uint8_t kCommandHciReadLocalVersionInformation[] = {0x01, 0x10,
- 0x00};
-static constexpr uint8_t kCommandHciReadBufferSize[] = {0x05, 0x10, 0x00};
-static constexpr uint8_t kCommandHciWriteLoopbackModeLocal[] = {0x02, 0x18,
- 0x01, 0x01};
-static constexpr uint8_t kCommandHciReset[] = {0x03, 0x0c, 0x00};
-static constexpr uint8_t kCommandHciSynchronousFlowControlEnable[] = {
- 0x2f, 0x0c, 0x01, 0x01};
-static constexpr uint8_t kCommandHciWriteLocalName[] = {0x13, 0x0c, 0xf8};
-static constexpr uint8_t kHciStatusSuccess = 0x00;
-static constexpr uint8_t kHciStatusUnknownHciCommand = 0x01;
-
-static constexpr uint8_t kEventConnectionComplete = 0x03;
-static constexpr uint8_t kEventCommandComplete = 0x0e;
-static constexpr uint8_t kEventCommandStatus = 0x0f;
-static constexpr uint8_t kEventNumberOfCompletedPackets = 0x13;
-static constexpr uint8_t kEventLoopbackCommand = 0x19;
-
-static constexpr size_t kEventCodeByte = 0;
-static constexpr size_t kEventLengthByte = 1;
-static constexpr size_t kEventFirstPayloadByte = 2;
-static constexpr size_t kEventCommandStatusStatusByte = 2;
-static constexpr size_t kEventCommandStatusOpcodeLsByte = 4; // Bytes 4 and 5
-static constexpr size_t kEventCommandCompleteOpcodeLsByte = 3; // Bytes 3 and 4
-static constexpr size_t kEventCommandCompleteStatusByte = 5;
-static constexpr size_t kEventCommandCompleteFirstParamByte = 6;
-static constexpr size_t kEventLocalHciVersionByte =
- kEventCommandCompleteFirstParamByte;
-static constexpr size_t kEventLocalLmpVersionByte =
- kEventLocalHciVersionByte + 3;
-
-static constexpr size_t kEventConnectionCompleteParamLength = 11;
-static constexpr size_t kEventConnectionCompleteType = 11;
-static constexpr size_t kEventConnectionCompleteTypeSco = 0;
-static constexpr size_t kEventConnectionCompleteTypeAcl = 1;
-static constexpr size_t kEventConnectionCompleteHandleLsByte = 3;
-
-static constexpr size_t kEventNumberOfCompletedPacketsNumHandles = 2;
-
-static constexpr size_t kAclBroadcastFlagOffset = 6;
-static constexpr uint8_t kAclBroadcastFlagPointToPoint = 0x0;
-static constexpr uint8_t kAclBroadcastPointToPoint =
- (kAclBroadcastFlagPointToPoint << kAclBroadcastFlagOffset);
-
-static constexpr uint8_t kAclPacketBoundaryFlagOffset = 4;
-static constexpr uint8_t kAclPacketBoundaryFlagFirstAutoFlushable = 0x2;
-static constexpr uint8_t kAclPacketBoundaryFirstAutoFlushable =
- kAclPacketBoundaryFlagFirstAutoFlushable << kAclPacketBoundaryFlagOffset;
-
// To discard Qualcomm ACL debugging
static constexpr uint16_t kAclHandleQcaDebugMessage = 0xedc;
class ThroughputLogger {
public:
- ThroughputLogger(std::string task)
+ explicit ThroughputLogger(std::string task)
: total_bytes_(0),
- task_(task),
+ task_(std::move(task)),
start_time_(std::chrono::steady_clock::now()) {}
~ThroughputLogger() {
@@ -142,7 +114,7 @@
// The main test class for Bluetooth HAL.
class BluetoothAidlTest : public ::testing::TestWithParam<std::string> {
public:
- virtual void SetUp() override {
+ void SetUp() override {
// currently test passthrough mode only
hci = IBluetoothHci::fromBinder(
SpAIBinder(AServiceManager_waitForService(GetParam().c_str())));
@@ -158,7 +130,7 @@
ASSERT_NE(bluetooth_hci_death_recipient, nullptr);
ASSERT_EQ(STATUS_OK,
AIBinder_linkToDeath(hci->asBinder().get(),
- bluetooth_hci_death_recipient, 0));
+ bluetooth_hci_death_recipient, nullptr));
hci_cb = ndk::SharedRefBase::make<BluetoothHciCallbacks>(*this);
ASSERT_NE(hci_cb, nullptr);
@@ -179,7 +151,7 @@
ASSERT_TRUE(future.get());
}
- virtual void TearDown() override {
+ void TearDown() override {
ALOGI("TearDown");
// Should not be checked in production code
ASSERT_TRUE(hci->close().isOk());
@@ -205,8 +177,14 @@
void handle_no_ops();
void discard_qca_debugging();
void wait_for_event(bool timeout_is_error);
- void wait_for_command_complete_event(std::vector<uint8_t> cmd);
+ void wait_for_command_complete_event(OpCode opCode,
+ std::vector<uint8_t>& complete_event);
+ // Wait until a command complete is received.
+ // Command complete will be consumed after this method
+ void wait_and_validate_command_complete_event(OpCode opCode);
int wait_for_completed_packets_event(uint16_t handle);
+ void send_and_wait_for_cmd_complete(std::unique_ptr<CommandBuilder> cmd,
+ std::vector<uint8_t>& cmd_complete);
// A simple test implementation of BluetoothHciCallbacks.
class BluetoothHciCallbacks
@@ -214,36 +192,41 @@
BluetoothAidlTest& parent_;
public:
- BluetoothHciCallbacks(BluetoothAidlTest& parent) : parent_(parent){};
+ explicit BluetoothHciCallbacks(BluetoothAidlTest& parent)
+ : parent_(parent){};
- virtual ~BluetoothHciCallbacks() = default;
+ ~BluetoothHciCallbacks() override = default;
- ndk::ScopedAStatus initializationComplete(Status status) {
+ ndk::ScopedAStatus initializationComplete(Status status) override {
parent_.initialized_promise.set_value(status == Status::SUCCESS);
ALOGV("%s (status = %d)", __func__, static_cast<int>(status));
return ScopedAStatus::ok();
};
- ndk::ScopedAStatus hciEventReceived(const std::vector<uint8_t>& event) {
+ ndk::ScopedAStatus hciEventReceived(
+ const std::vector<uint8_t>& event) override {
parent_.event_cb_count++;
parent_.event_queue.push(event);
- ALOGV("Event received (length = %d)", static_cast<int>(event.size()));
+ ALOGI("Event received (length = %d)", static_cast<int>(event.size()));
return ScopedAStatus::ok();
};
- ndk::ScopedAStatus aclDataReceived(const std::vector<uint8_t>& data) {
+ ndk::ScopedAStatus aclDataReceived(
+ const std::vector<uint8_t>& data) override {
parent_.acl_cb_count++;
parent_.acl_queue.push(data);
return ScopedAStatus::ok();
};
- ndk::ScopedAStatus scoDataReceived(const std::vector<uint8_t>& data) {
+ ndk::ScopedAStatus scoDataReceived(
+ const std::vector<uint8_t>& data) override {
parent_.sco_cb_count++;
parent_.sco_queue.push(data);
return ScopedAStatus::ok();
};
- ndk::ScopedAStatus isoDataReceived(const std::vector<uint8_t>& data) {
+ ndk::ScopedAStatus isoDataReceived(
+ const std::vector<uint8_t>& data) override {
parent_.iso_cb_count++;
parent_.iso_queue.push(data);
return ScopedAStatus::ok();
@@ -253,7 +236,8 @@
template <class T>
class WaitQueue {
public:
- WaitQueue(){};
+ WaitQueue() = default;
+ ;
virtual ~WaitQueue() = default;
@@ -283,6 +267,14 @@
return true;
};
+ void pop() {
+ std::lock_guard<std::mutex> lock(m_);
+ if (q_.empty()) {
+ return;
+ }
+ q_.pop();
+ };
+
bool front(T& v) {
std::lock_guard<std::mutex> lock(m_);
if (q_.empty()) {
@@ -329,22 +321,22 @@
std::shared_ptr<IBluetoothHci> hci;
std::shared_ptr<BluetoothHciCallbacks> hci_cb;
- AIBinder_DeathRecipient* bluetooth_hci_death_recipient;
+ AIBinder_DeathRecipient* bluetooth_hci_death_recipient{};
WaitQueue<std::vector<uint8_t>> event_queue;
WaitQueue<std::vector<uint8_t>> acl_queue;
WaitQueue<std::vector<uint8_t>> sco_queue;
WaitQueue<std::vector<uint8_t>> iso_queue;
std::promise<bool> initialized_promise;
- int event_cb_count;
- int sco_cb_count;
- int acl_cb_count;
- int iso_cb_count;
+ int event_cb_count{};
+ int sco_cb_count{};
+ int acl_cb_count{};
+ int iso_cb_count{};
- int max_acl_data_packet_length;
- int max_sco_data_packet_length;
- int max_acl_data_packets;
- int max_sco_data_packets;
+ int max_acl_data_packet_length{};
+ int max_sco_data_packet_length{};
+ int max_acl_data_packets{};
+ int max_sco_data_packets{};
std::vector<uint16_t> sco_connection_handles;
std::vector<uint16_t> acl_connection_handles;
@@ -355,17 +347,20 @@
while (!event_queue.empty()) {
std::vector<uint8_t> event;
event_queue.front(event);
- ASSERT_GE(event.size(),
- static_cast<size_t>(kEventCommandCompleteStatusByte));
- bool event_is_no_op =
- (event[kEventCodeByte] == kEventCommandComplete) &&
- (event[kEventCommandCompleteOpcodeLsByte] == 0x00) &&
- (event[kEventCommandCompleteOpcodeLsByte + 1] == 0x00);
- event_is_no_op |= (event[kEventCodeByte] == kEventCommandStatus) &&
- (event[kEventCommandStatusOpcodeLsByte] == 0x00) &&
- (event[kEventCommandStatusOpcodeLsByte + 1] == 0x00);
- if (event_is_no_op) {
- event_queue.pop(event);
+ auto complete_view = ::bluetooth::hci::CommandCompleteView::Create(
+ ::bluetooth::hci::EventView::Create(::bluetooth::hci::PacketView<true>(
+ std::make_shared<std::vector<uint8_t>>(event))));
+ auto status_view = ::bluetooth::hci::CommandCompleteView::Create(
+ ::bluetooth::hci::EventView::Create(::bluetooth::hci::PacketView<true>(
+ std::make_shared<std::vector<uint8_t>>(event))));
+ bool is_complete_no_op =
+ complete_view.IsValid() &&
+ complete_view.GetCommandOpCode() == ::bluetooth::hci::OpCode::NONE;
+ bool is_status_no_op =
+ status_view.IsValid() &&
+ status_view.GetCommandOpCode() == ::bluetooth::hci::OpCode::NONE;
+ if (is_complete_no_op || is_status_no_op) {
+ event_queue.pop();
} else {
break;
}
@@ -377,12 +372,12 @@
while (!acl_queue.empty()) {
std::vector<uint8_t> acl_packet;
acl_queue.front(acl_packet);
- uint16_t connection_handle = acl_packet[1] & 0xF;
- connection_handle <<= 8;
- connection_handle |= acl_packet[0];
- bool packet_is_no_op = connection_handle == kAclHandleQcaDebugMessage;
- if (packet_is_no_op) {
- acl_queue.pop(acl_packet);
+ auto acl_view =
+ ::bluetooth::hci::AclView::Create(::bluetooth::hci::PacketView<true>(
+ std::make_shared<std::vector<uint8_t>>(acl_packet)));
+ EXPECT_TRUE(acl_view.IsValid());
+ if (acl_view.GetHandle() == kAclHandleQcaDebugMessage) {
+ acl_queue.pop();
} else {
break;
}
@@ -407,48 +402,49 @@
}
}
-// Wait until a command complete is received.
void BluetoothAidlTest::wait_for_command_complete_event(
- std::vector<uint8_t> cmd) {
+ OpCode opCode, std::vector<uint8_t>& complete_event) {
ASSERT_NO_FATAL_FAILURE(wait_for_event());
- std::vector<uint8_t> event;
ASSERT_FALSE(event_queue.empty());
- ASSERT_TRUE(event_queue.pop(event));
+ ASSERT_TRUE(event_queue.pop(complete_event));
+ auto complete_view = ::bluetooth::hci::CommandCompleteView::Create(
+ ::bluetooth::hci::EventView::Create(::bluetooth::hci::PacketView<true>(
+ std::make_shared<std::vector<uint8_t>>(complete_event))));
+ ASSERT_TRUE(complete_view.IsValid());
+ ASSERT_EQ(complete_view.GetCommandOpCode(), opCode);
+ ASSERT_EQ(complete_view.GetPayload()[0],
+ static_cast<uint8_t>(::bluetooth::hci::ErrorCode::SUCCESS));
+}
- ASSERT_GT(event.size(), static_cast<size_t>(kEventCommandCompleteStatusByte));
- ASSERT_EQ(kEventCommandComplete, event[kEventCodeByte]);
- ASSERT_EQ(cmd[0], event[kEventCommandCompleteOpcodeLsByte]);
- ASSERT_EQ(cmd[1], event[kEventCommandCompleteOpcodeLsByte + 1]);
- ASSERT_EQ(kHciStatusSuccess, event[kEventCommandCompleteStatusByte]);
+void BluetoothAidlTest::wait_and_validate_command_complete_event(
+ ::bluetooth::hci::OpCode opCode) {
+ std::vector<uint8_t> complete_event;
+ ASSERT_NO_FATAL_FAILURE(
+ wait_for_command_complete_event(opCode, complete_event));
}
// Send the command to read the controller's buffer sizes.
void BluetoothAidlTest::setBufferSizes() {
- std::vector<uint8_t> cmd{
- kCommandHciReadBufferSize,
- kCommandHciReadBufferSize + sizeof(kCommandHciReadBufferSize)};
+ std::vector<uint8_t> cmd;
+ ::bluetooth::packet::BitInserter bi{cmd};
+ ::bluetooth::hci::ReadBufferSizeBuilder::Create()->Serialize(bi);
hci->sendHciCommand(cmd);
ASSERT_NO_FATAL_FAILURE(wait_for_event());
- if (event_queue.empty()) {
- return;
- }
std::vector<uint8_t> event;
ASSERT_TRUE(event_queue.pop(event));
+ auto complete_view = ::bluetooth::hci::ReadBufferSizeCompleteView::Create(
+ ::bluetooth::hci::CommandCompleteView::Create(
+ ::bluetooth::hci::EventView::Create(
+ ::bluetooth::hci::PacketView<true>(
+ std::make_shared<std::vector<uint8_t>>(event)))));
- ASSERT_EQ(kEventCommandComplete, event[kEventCodeByte]);
- ASSERT_EQ(cmd[0], event[kEventCommandCompleteOpcodeLsByte]);
- ASSERT_EQ(cmd[1], event[kEventCommandCompleteOpcodeLsByte + 1]);
- ASSERT_EQ(kHciStatusSuccess, event[kEventCommandCompleteStatusByte]);
-
- max_acl_data_packet_length =
- event[kEventCommandCompleteStatusByte + 1] +
- (event[kEventCommandCompleteStatusByte + 2] << 8);
- max_sco_data_packet_length = event[kEventCommandCompleteStatusByte + 3];
- max_acl_data_packets = event[kEventCommandCompleteStatusByte + 4] +
- (event[kEventCommandCompleteStatusByte + 5] << 8);
- max_sco_data_packets = event[kEventCommandCompleteStatusByte + 6] +
- (event[kEventCommandCompleteStatusByte + 7] << 8);
+ ASSERT_TRUE(complete_view.IsValid());
+ ASSERT_EQ(complete_view.GetStatus(), ::bluetooth::hci::ErrorCode::SUCCESS);
+ max_acl_data_packet_length = complete_view.GetAclDataPacketLength();
+ max_sco_data_packet_length = complete_view.GetSynchronousDataPacketLength();
+ max_acl_data_packets = complete_view.GetTotalNumAclDataPackets();
+ max_sco_data_packets = complete_view.GetTotalNumSynchronousDataPackets();
ALOGD("%s: ACL max %d num %d SCO max %d num %d", __func__,
static_cast<int>(max_acl_data_packet_length),
@@ -459,39 +455,39 @@
// Enable flow control packets for SCO
void BluetoothAidlTest::setSynchronousFlowControlEnable() {
- std::vector<uint8_t> cmd{kCommandHciSynchronousFlowControlEnable,
- kCommandHciSynchronousFlowControlEnable +
- sizeof(kCommandHciSynchronousFlowControlEnable)};
+ std::vector<uint8_t> cmd;
+ ::bluetooth::packet::BitInserter bi{cmd};
+ ::bluetooth::hci::WriteSynchronousFlowControlEnableBuilder::Create(
+ ::bluetooth::hci::Enable::ENABLED)
+ ->Serialize(bi);
hci->sendHciCommand(cmd);
- wait_for_command_complete_event(cmd);
+ wait_and_validate_command_complete_event(
+ ::bluetooth::hci::OpCode::WRITE_SYNCHRONOUS_FLOW_CONTROL_ENABLE);
}
// Send an HCI command (in Loopback mode) and check the response.
void BluetoothAidlTest::sendAndCheckHci(int num_packets) {
- ThroughputLogger logger = {__func__};
- int command_size = 0;
+ ThroughputLogger logger{__func__};
+ size_t command_size = 0;
+ char new_name[] = "John Jacob Jingleheimer Schmidt ___________________";
+ size_t new_name_length = strlen(new_name);
for (int n = 0; n < num_packets; n++) {
- // Send an HCI packet
- std::vector<uint8_t> write_name{
- kCommandHciWriteLocalName,
- kCommandHciWriteLocalName + sizeof(kCommandHciWriteLocalName)};
- // With a name
- char new_name[] = "John Jacob Jingleheimer Schmidt ___________________0";
- size_t new_name_length = strlen(new_name);
+ // The name to set is new_name
+ std::array<uint8_t, 248> name_array{};
for (size_t i = 0; i < new_name_length; i++) {
- write_name.push_back(static_cast<uint8_t>(new_name[i]));
+ name_array[i] = new_name[i];
}
// And the packet number
- size_t i = new_name_length - 1;
- for (int digits = n; digits > 0; digits = digits / 10, i--) {
- write_name[i] = static_cast<uint8_t>('0' + digits % 10);
+ char number[11] = "0000000000";
+ snprintf(number, sizeof(number), "%010d", static_cast<int>(n));
+ for (size_t i = new_name_length; i < new_name_length + sizeof(number) - 1;
+ i++) {
+ name_array[new_name_length + i] = number[i];
}
- // And padding
- for (size_t i = 0; i < 248 - new_name_length; i++) {
- write_name.push_back(static_cast<uint8_t>(0));
- }
-
+ std::vector<uint8_t> write_name;
+ ::bluetooth::packet::BitInserter bi{write_name};
+ ::bluetooth::hci::WriteLocalNameBuilder::Create(name_array)->Serialize(bi);
hci->sendHciCommand(write_name);
// Check the loopback of the HCI packet
@@ -499,28 +495,17 @@
std::vector<uint8_t> event;
ASSERT_TRUE(event_queue.pop(event));
-
- size_t compare_length = (write_name.size() > static_cast<size_t>(0xff)
- ? static_cast<size_t>(0xff)
- : write_name.size());
- ASSERT_GT(event.size(), compare_length + kEventFirstPayloadByte - 1);
-
- ASSERT_EQ(kEventLoopbackCommand, event[kEventCodeByte]);
- ASSERT_EQ(compare_length, event[kEventLengthByte]);
-
- // Don't compare past the end of the event.
- if (compare_length + kEventFirstPayloadByte > event.size()) {
- compare_length = event.size() - kEventFirstPayloadByte;
- ALOGE("Only comparing %d bytes", static_cast<int>(compare_length));
- }
+ auto event_view = ::bluetooth::hci::LoopbackCommandView::Create(
+ ::bluetooth::hci::EventView::Create(::bluetooth::hci::PacketView<true>(
+ std::make_shared<std::vector<uint8_t>>(event))));
+ ASSERT_TRUE(event_view.IsValid());
+ std::vector<uint8_t> looped_back_command{event_view.GetPayload().begin(),
+ event_view.GetPayload().end()};
+ ASSERT_EQ(looped_back_command, write_name);
if (n == num_packets - 1) {
command_size = write_name.size();
}
-
- for (size_t i = 0; i < compare_length; i++) {
- ASSERT_EQ(write_name[i], event[kEventFirstPayloadByte + i]);
- }
}
logger.setTotalBytes(command_size * num_packets * 2);
}
@@ -528,16 +513,18 @@
// Send a SCO data packet (in Loopback mode) and check the response.
void BluetoothAidlTest::sendAndCheckSco(int num_packets, size_t size,
uint16_t handle) {
- ThroughputLogger logger = {__func__};
+ ThroughputLogger logger{__func__};
for (int n = 0; n < num_packets; n++) {
// Send a SCO packet
std::vector<uint8_t> sco_packet;
- sco_packet.push_back(static_cast<uint8_t>(handle & 0xff));
- sco_packet.push_back(static_cast<uint8_t>((handle & 0x0f00) >> 8));
- sco_packet.push_back(static_cast<uint8_t>(size & 0xff));
+ std::vector<uint8_t> payload;
for (size_t i = 0; i < size; i++) {
- sco_packet.push_back(static_cast<uint8_t>(i + n));
+ payload.push_back(static_cast<uint8_t>(i + n));
}
+ ::bluetooth::packet::BitInserter bi{sco_packet};
+ ::bluetooth::hci::ScoBuilder::Create(
+ handle, ::bluetooth::hci::PacketStatusFlag::CORRECTLY_RECEIVED, payload)
+ ->Serialize(bi);
hci->sendScoData(sco_packet);
// Check the loopback of the SCO packet
@@ -545,21 +532,7 @@
ASSERT_TRUE(
sco_queue.tryPopWithTimeout(sco_loopback, kWaitForScoDataTimeout));
- ASSERT_EQ(sco_packet.size(), sco_loopback.size());
- size_t successful_bytes = 0;
-
- for (size_t i = 0; i < sco_packet.size(); i++) {
- if (sco_packet[i] == sco_loopback[i]) {
- successful_bytes = i;
- } else {
- ALOGE("Miscompare at %d (expected %x, got %x)", static_cast<int>(i),
- sco_packet[i], sco_loopback[i]);
- ALOGE("At %d (expected %x, got %x)", static_cast<int>(i + 1),
- sco_packet[i + 1], sco_loopback[i + 1]);
- break;
- }
- }
- ASSERT_EQ(sco_packet.size(), successful_bytes + 1);
+ ASSERT_EQ(sco_packet, sco_loopback);
}
logger.setTotalBytes(num_packets * size * 2);
}
@@ -567,19 +540,20 @@
// Send an ACL data packet (in Loopback mode) and check the response.
void BluetoothAidlTest::sendAndCheckAcl(int num_packets, size_t size,
uint16_t handle) {
- ThroughputLogger logger = {__func__};
+ ThroughputLogger logger{__func__};
for (int n = 0; n < num_packets; n++) {
- // Send an ACL packet
- std::vector<uint8_t> acl_packet;
- acl_packet.push_back(static_cast<uint8_t>(handle & 0xff));
- acl_packet.push_back(static_cast<uint8_t>((handle & 0x0f00) >> 8) |
- kAclBroadcastPointToPoint |
- kAclPacketBoundaryFirstAutoFlushable);
- acl_packet.push_back(static_cast<uint8_t>(size & 0xff));
- acl_packet.push_back(static_cast<uint8_t>((size & 0xff00) >> 8));
+ // Send an ACL packet with counting data
+ auto payload = std::make_unique<::bluetooth::packet::RawBuilder>();
for (size_t i = 0; i < size; i++) {
- acl_packet.push_back(static_cast<uint8_t>(i + n));
+ payload->AddOctets1(static_cast<uint8_t>(i + n));
}
+ std::vector<uint8_t> acl_packet;
+ ::bluetooth::packet::BitInserter bi{acl_packet};
+ ::bluetooth::hci::AclBuilder::Create(
+ handle,
+ ::bluetooth::hci::PacketBoundaryFlag::FIRST_AUTOMATICALLY_FLUSHABLE,
+ ::bluetooth::hci::BroadcastFlag::POINT_TO_POINT, std::move(payload))
+ ->Serialize(bi);
hci->sendAclData(acl_packet);
std::vector<uint8_t> acl_loopback;
@@ -587,21 +561,7 @@
ASSERT_TRUE(
acl_queue.tryPopWithTimeout(acl_loopback, kWaitForAclDataTimeout));
- ASSERT_EQ(acl_packet.size(), acl_loopback.size());
- size_t successful_bytes = 0;
-
- for (size_t i = 0; i < acl_packet.size(); i++) {
- if (acl_packet[i] == acl_loopback[i]) {
- successful_bytes = i;
- } else {
- ALOGE("Miscompare at %d (expected %x, got %x)", static_cast<int>(i),
- acl_packet[i], acl_loopback[i]);
- ALOGE("At %d (expected %x, got %x)", static_cast<int>(i + 1),
- acl_packet[i + 1], acl_loopback[i + 1]);
- break;
- }
- }
- ASSERT_EQ(acl_packet.size(), successful_bytes + 1);
+ ASSERT_EQ(acl_packet, acl_loopback);
}
logger.setTotalBytes(num_packets * size * 2);
}
@@ -620,23 +580,29 @@
}
std::vector<uint8_t> event;
EXPECT_TRUE(event_queue.pop(event));
-
- EXPECT_EQ(kEventNumberOfCompletedPackets, event[kEventCodeByte]);
- EXPECT_EQ(1, event[kEventNumberOfCompletedPacketsNumHandles]);
-
- uint16_t event_handle = event[3] + (event[4] << 8);
- EXPECT_EQ(handle, event_handle);
-
- packets_processed += event[5] + (event[6] << 8);
+ auto event_view = ::bluetooth::hci::NumberOfCompletedPacketsView::Create(
+ ::bluetooth::hci::EventView::Create(::bluetooth::hci::PacketView<true>(
+ std::make_shared<std::vector<uint8_t>>(event))));
+ if (!event_view.IsValid()) {
+ ADD_FAILURE();
+ return packets_processed;
+ }
+ auto completed_packets = event_view.GetCompletedPackets();
+ for (const auto& entry : completed_packets) {
+ EXPECT_EQ(handle, entry.connection_handle_);
+ packets_processed += entry.host_num_of_completed_packets_;
+ }
}
return packets_processed;
}
// Send local loopback command and initialize SCO and ACL handles.
void BluetoothAidlTest::enterLoopbackMode() {
- std::vector<uint8_t> cmd{kCommandHciWriteLoopbackModeLocal,
- kCommandHciWriteLoopbackModeLocal +
- sizeof(kCommandHciWriteLoopbackModeLocal)};
+ std::vector<uint8_t> cmd;
+ ::bluetooth::packet::BitInserter bi{cmd};
+ ::bluetooth::hci::WriteLoopbackModeBuilder::Create(
+ bluetooth::hci::LoopbackMode::ENABLE_LOCAL)
+ ->Serialize(bi);
hci->sendHciCommand(cmd);
// Receive connection complete events with data channels
@@ -652,97 +618,128 @@
}
std::vector<uint8_t> event;
ASSERT_TRUE(event_queue.pop(event));
- ASSERT_GT(event.size(),
- static_cast<size_t>(kEventCommandCompleteStatusByte));
- if (event[kEventCodeByte] == kEventConnectionComplete) {
- ASSERT_GT(event.size(),
- static_cast<size_t>(kEventConnectionCompleteType));
- ASSERT_EQ(event[kEventLengthByte], kEventConnectionCompleteParamLength);
- uint8_t connection_type = event[kEventConnectionCompleteType];
+ auto event_view =
+ ::bluetooth::hci::EventView::Create(::bluetooth::hci::PacketView<true>(
+ std::make_shared<std::vector<uint8_t>>(event)));
+ ASSERT_TRUE(event_view.IsValid());
- ASSERT_TRUE(connection_type == kEventConnectionCompleteTypeSco ||
- connection_type == kEventConnectionCompleteTypeAcl);
-
- // Save handles
- uint16_t handle = event[kEventConnectionCompleteHandleLsByte] |
- event[kEventConnectionCompleteHandleLsByte + 1] << 8;
- if (connection_type == kEventConnectionCompleteTypeSco) {
- sco_connection_handles.push_back(handle);
- } else {
- acl_connection_handles.push_back(handle);
+ if (event_view.GetEventCode() ==
+ ::bluetooth::hci::EventCode::CONNECTION_COMPLETE) {
+ auto complete_view =
+ ::bluetooth::hci::ConnectionCompleteView::Create(event_view);
+ ASSERT_TRUE(complete_view.IsValid());
+ switch (complete_view.GetLinkType()) {
+ case ::bluetooth::hci::LinkType::ACL:
+ acl_connection_handles.push_back(complete_view.GetConnectionHandle());
+ break;
+ case ::bluetooth::hci::LinkType::SCO:
+ sco_connection_handles.push_back(complete_view.GetConnectionHandle());
+ break;
+ default:
+ ASSERT_EQ(complete_view.GetLinkType(),
+ ::bluetooth::hci::LinkType::ACL);
}
-
- ALOGD("Connect complete type = %d handle = %d",
- event[kEventConnectionCompleteType], handle);
connection_event_count++;
} else {
- ASSERT_EQ(kEventCommandComplete, event[kEventCodeByte]);
- ASSERT_EQ(cmd[0], event[kEventCommandCompleteOpcodeLsByte]);
- ASSERT_EQ(cmd[1], event[kEventCommandCompleteOpcodeLsByte + 1]);
- ASSERT_EQ(kHciStatusSuccess, event[kEventCommandCompleteStatusByte]);
+ auto command_complete_view =
+ ::bluetooth::hci::WriteLoopbackModeCompleteView::Create(
+ ::bluetooth::hci::CommandCompleteView::Create(event_view));
+ ASSERT_TRUE(command_complete_view.IsValid());
+ ASSERT_EQ(::bluetooth::hci::ErrorCode::SUCCESS,
+ command_complete_view.GetStatus());
command_complete_received = true;
}
}
}
+void BluetoothAidlTest::send_and_wait_for_cmd_complete(
+ std::unique_ptr<CommandBuilder> cmd, std::vector<uint8_t>& cmd_complete) {
+ std::vector<uint8_t> cmd_bytes = cmd->SerializeToBytes();
+ hci->sendHciCommand(cmd_bytes);
+
+ auto view = CommandView::Create(
+ PacketView<true>(std::make_shared<std::vector<uint8_t>>(cmd_bytes)));
+ ASSERT_TRUE(view.IsValid());
+ ALOGI("Waiting for %s[0x%x]", OpCodeText(view.GetOpCode()).c_str(),
+ static_cast<int>(view.GetOpCode()));
+ ASSERT_NO_FATAL_FAILURE(
+ wait_for_command_complete_event(view.GetOpCode(), cmd_complete));
+}
+
// Empty test: Initialize()/Close() are called in SetUp()/TearDown().
TEST_P(BluetoothAidlTest, InitializeAndClose) {}
// Send an HCI Reset with sendHciCommand and wait for a command complete event.
TEST_P(BluetoothAidlTest, HciReset) {
- std::vector<uint8_t> reset{kCommandHciReset,
- kCommandHciReset + sizeof(kCommandHciReset)};
+ std::vector<uint8_t> reset;
+ ::bluetooth::packet::BitInserter bi{reset};
+ ::bluetooth::hci::ResetBuilder::Create()->Serialize(bi);
hci->sendHciCommand(reset);
- wait_for_command_complete_event(reset);
+ wait_and_validate_command_complete_event(::bluetooth::hci::OpCode::RESET);
}
// Read and check the HCI version of the controller.
TEST_P(BluetoothAidlTest, HciVersionTest) {
- std::vector<uint8_t> cmd{kCommandHciReadLocalVersionInformation,
- kCommandHciReadLocalVersionInformation +
- sizeof(kCommandHciReadLocalVersionInformation)};
+ std::vector<uint8_t> cmd;
+ ::bluetooth::packet::BitInserter bi{cmd};
+ ::bluetooth::hci::ReadLocalVersionInformationBuilder::Create()->Serialize(bi);
hci->sendHciCommand(cmd);
ASSERT_NO_FATAL_FAILURE(wait_for_event());
std::vector<uint8_t> event;
ASSERT_TRUE(event_queue.pop(event));
- ASSERT_GT(event.size(), static_cast<size_t>(kEventLocalLmpVersionByte));
-
- ASSERT_EQ(kEventCommandComplete, event[kEventCodeByte]);
- ASSERT_EQ(cmd[0], event[kEventCommandCompleteOpcodeLsByte]);
- ASSERT_EQ(cmd[1], event[kEventCommandCompleteOpcodeLsByte + 1]);
- ASSERT_EQ(kHciStatusSuccess, event[kEventCommandCompleteStatusByte]);
-
- ASSERT_LE(kHciMinimumHciVersion, event[kEventLocalHciVersionByte]);
- ASSERT_LE(kHciMinimumLmpVersion, event[kEventLocalLmpVersionByte]);
+ auto complete_view =
+ ::bluetooth::hci::ReadLocalVersionInformationCompleteView::Create(
+ ::bluetooth::hci::CommandCompleteView::Create(
+ ::bluetooth::hci::EventView::Create(
+ ::bluetooth::hci::PacketView<true>(
+ std::make_shared<std::vector<uint8_t>>(event)))));
+ ASSERT_TRUE(complete_view.IsValid());
+ ASSERT_EQ(::bluetooth::hci::ErrorCode::SUCCESS, complete_view.GetStatus());
+ auto version = complete_view.GetLocalVersionInformation();
+ ASSERT_LE(::bluetooth::hci::HciVersion::V_3_0, version.hci_version_);
+ ASSERT_LE(::bluetooth::hci::LmpVersion::V_3_0, version.lmp_version_);
}
// Send an unknown HCI command and wait for the error message.
TEST_P(BluetoothAidlTest, HciUnknownCommand) {
- std::vector<uint8_t> cmd{
- kCommandHciShouldBeUnknown,
- kCommandHciShouldBeUnknown + sizeof(kCommandHciShouldBeUnknown)};
+ std::vector<uint8_t> cmd;
+ ::bluetooth::packet::BitInserter bi{cmd};
+ ::bluetooth::hci::CommandBuilder::Create(
+ static_cast<::bluetooth::hci::OpCode>(0x3cff),
+ std::make_unique<::bluetooth::packet::RawBuilder>())
+ ->Serialize(bi);
hci->sendHciCommand(cmd);
ASSERT_NO_FATAL_FAILURE(wait_for_event());
std::vector<uint8_t> event;
ASSERT_TRUE(event_queue.pop(event));
+ auto event_view =
+ ::bluetooth::hci::EventView::Create(::bluetooth::hci::PacketView<true>(
+ std::make_shared<std::vector<uint8_t>>(event)));
+ ASSERT_TRUE(event_view.IsValid());
- ASSERT_GT(event.size(), static_cast<size_t>(kEventCommandCompleteStatusByte));
- if (event[kEventCodeByte] == kEventCommandComplete) {
- ASSERT_EQ(cmd[0], event[kEventCommandCompleteOpcodeLsByte]);
- ASSERT_EQ(cmd[1], event[kEventCommandCompleteOpcodeLsByte + 1]);
- ASSERT_EQ(kHciStatusUnknownHciCommand,
- event[kEventCommandCompleteStatusByte]);
- } else {
- ASSERT_EQ(kEventCommandStatus, event[kEventCodeByte]);
- ASSERT_EQ(cmd[0], event[kEventCommandStatusOpcodeLsByte]);
- ASSERT_EQ(cmd[1], event[kEventCommandStatusOpcodeLsByte + 1]);
- ASSERT_EQ(kHciStatusUnknownHciCommand,
- event[kEventCommandStatusStatusByte]);
+ switch (event_view.GetEventCode()) {
+ case ::bluetooth::hci::EventCode::COMMAND_COMPLETE: {
+ auto command_complete =
+ ::bluetooth::hci::CommandCompleteView::Create(event_view);
+ ASSERT_TRUE(command_complete.IsValid());
+ ASSERT_EQ(command_complete.GetPayload()[0],
+ static_cast<uint8_t>(
+ ::bluetooth::hci::ErrorCode::UNKNOWN_HCI_COMMAND));
+ } break;
+ case ::bluetooth::hci::EventCode::COMMAND_STATUS: {
+ auto command_status =
+ ::bluetooth::hci::CommandStatusView::Create(event_view);
+ ASSERT_TRUE(command_status.IsValid());
+ ASSERT_EQ(command_status.GetStatus(),
+ ::bluetooth::hci::ErrorCode::UNKNOWN_HCI_COMMAND);
+ } break;
+ default:
+ ADD_FAILURE();
}
}
@@ -851,20 +848,24 @@
// Set all bits in the event mask
TEST_P(BluetoothAidlTest, SetEventMask) {
- std::vector<uint8_t> set_event_mask{
- 0x01, 0x0c, 0x08 /*parameter bytes*/, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff};
- hci->sendHciCommand({set_event_mask});
- wait_for_command_complete_event(set_event_mask);
+ std::vector<uint8_t> cmd;
+ ::bluetooth::packet::BitInserter bi{cmd};
+ uint64_t full_mask = UINT64_MAX;
+ ::bluetooth::hci::SetEventMaskBuilder::Create(full_mask)->Serialize(bi);
+ hci->sendHciCommand(cmd);
+ wait_and_validate_command_complete_event(
+ ::bluetooth::hci::OpCode::SET_EVENT_MASK);
}
// Set all bits in the LE event mask
TEST_P(BluetoothAidlTest, SetLeEventMask) {
- std::vector<uint8_t> set_event_mask{
- 0x20, 0x0c, 0x08 /*parameter bytes*/, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff};
- hci->sendHciCommand({set_event_mask});
- wait_for_command_complete_event(set_event_mask);
+ std::vector<uint8_t> cmd;
+ ::bluetooth::packet::BitInserter bi{cmd};
+ uint64_t full_mask = UINT64_MAX;
+ ::bluetooth::hci::LeSetEventMaskBuilder::Create(full_mask)->Serialize(bi);
+ hci->sendHciCommand(cmd);
+ wait_and_validate_command_complete_event(
+ ::bluetooth::hci::OpCode::LE_SET_EVENT_MASK);
}
// Call initialize twice, second one should fail.
@@ -872,28 +873,32 @@
class SecondCb
: public aidl::android::hardware::bluetooth::BnBluetoothHciCallbacks {
public:
- ndk::ScopedAStatus initializationComplete(Status status) {
+ ndk::ScopedAStatus initializationComplete(Status status) override {
EXPECT_EQ(status, Status::ALREADY_INITIALIZED);
init_promise.set_value();
return ScopedAStatus::ok();
};
- ndk::ScopedAStatus hciEventReceived(const std::vector<uint8_t>& /*event*/) {
+ ndk::ScopedAStatus hciEventReceived(
+ const std::vector<uint8_t>& /*event*/) override {
ADD_FAILURE();
return ScopedAStatus::ok();
};
- ndk::ScopedAStatus aclDataReceived(const std::vector<uint8_t>& /*data*/) {
+ ndk::ScopedAStatus aclDataReceived(
+ const std::vector<uint8_t>& /*data*/) override {
ADD_FAILURE();
return ScopedAStatus::ok();
};
- ndk::ScopedAStatus scoDataReceived(const std::vector<uint8_t>& /*data*/) {
+ ndk::ScopedAStatus scoDataReceived(
+ const std::vector<uint8_t>& /*data*/) override {
ADD_FAILURE();
return ScopedAStatus::ok();
};
- ndk::ScopedAStatus isoDataReceived(const std::vector<uint8_t>& /*data*/) {
+ ndk::ScopedAStatus isoDataReceived(
+ const std::vector<uint8_t>& /*data*/) override {
ADD_FAILURE();
return ScopedAStatus::ok();
};
@@ -909,6 +914,66 @@
ASSERT_EQ(status, std::future_status::ready);
}
+TEST_P(BluetoothAidlTest, Cdd_C_12_1_Bluetooth5Requirements) {
+ std::vector<uint8_t> version_event;
+ send_and_wait_for_cmd_complete(ReadLocalVersionInformationBuilder::Create(),
+ version_event);
+ auto version_view = ReadLocalVersionInformationCompleteView::Create(
+ CommandCompleteView::Create(EventView::Create(PacketView<true>(
+ std::make_shared<std::vector<uint8_t>>(version_event)))));
+ ASSERT_TRUE(version_view.IsValid());
+ ASSERT_EQ(::bluetooth::hci::ErrorCode::SUCCESS, version_view.GetStatus());
+ auto version = version_view.GetLocalVersionInformation();
+ if (version.hci_version_ < ::bluetooth::hci::HciVersion::V_5_0) {
+ // This test does not apply to controllers below 5.0
+ return;
+ };
+ // When HCI version is 5.0, LMP version must also be at least 5.0
+ ASSERT_GE(static_cast<int>(version.lmp_version_),
+ static_cast<int>(version.hci_version_));
+
+ std::vector<uint8_t> le_features_event;
+ send_and_wait_for_cmd_complete(LeReadLocalSupportedFeaturesBuilder::Create(),
+ le_features_event);
+ auto le_features_view = LeReadLocalSupportedFeaturesCompleteView::Create(
+ CommandCompleteView::Create(EventView::Create(PacketView<true>(
+ std::make_shared<std::vector<uint8_t>>(le_features_event)))));
+ ASSERT_TRUE(le_features_view.IsValid());
+ ASSERT_EQ(::bluetooth::hci::ErrorCode::SUCCESS, le_features_view.GetStatus());
+ auto le_features = le_features_view.GetLeFeatures();
+ ASSERT_TRUE(le_features & static_cast<uint64_t>(LLFeaturesBits::LL_PRIVACY));
+ ASSERT_TRUE(le_features & static_cast<uint64_t>(LLFeaturesBits::LE_2M_PHY));
+ ASSERT_TRUE(le_features &
+ static_cast<uint64_t>(LLFeaturesBits::LE_CODED_PHY));
+ ASSERT_TRUE(le_features &
+ static_cast<uint64_t>(LLFeaturesBits::LE_EXTENDED_ADVERTISING));
+
+ std::vector<uint8_t> num_adv_set_event;
+ send_and_wait_for_cmd_complete(
+ LeReadNumberOfSupportedAdvertisingSetsBuilder::Create(),
+ num_adv_set_event);
+ auto num_adv_set_view =
+ LeReadNumberOfSupportedAdvertisingSetsCompleteView::Create(
+ CommandCompleteView::Create(EventView::Create(PacketView<true>(
+ std::make_shared<std::vector<uint8_t>>(num_adv_set_event)))));
+ ASSERT_TRUE(num_adv_set_view.IsValid());
+ ASSERT_EQ(::bluetooth::hci::ErrorCode::SUCCESS, num_adv_set_view.GetStatus());
+ auto num_adv_set = num_adv_set_view.GetNumberSupportedAdvertisingSets();
+ ASSERT_GE(num_adv_set, kMinLeAdvSetForBt5);
+
+ std::vector<uint8_t> num_resolving_list_event;
+ send_and_wait_for_cmd_complete(LeReadResolvingListSizeBuilder::Create(),
+ num_resolving_list_event);
+ auto num_resolving_list_view = LeReadResolvingListSizeCompleteView::Create(
+ CommandCompleteView::Create(EventView::Create(PacketView<true>(
+ std::make_shared<std::vector<uint8_t>>(num_resolving_list_event)))));
+ ASSERT_TRUE(num_resolving_list_view.IsValid());
+ ASSERT_EQ(::bluetooth::hci::ErrorCode::SUCCESS,
+ num_resolving_list_view.GetStatus());
+ auto num_resolving_list = num_resolving_list_view.GetResolvingListSize();
+ ASSERT_GE(num_resolving_list, kMinLeResolvingListForBt5);
+}
+
GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(BluetoothAidlTest);
INSTANTIATE_TEST_SUITE_P(PerInstance, BluetoothAidlTest,
testing::ValuesIn(android::getAidlHalInstanceNames(
diff --git a/broadcastradio/aidl/android/hardware/broadcastradio/AmFmRegionConfig.aidl b/broadcastradio/aidl/android/hardware/broadcastradio/AmFmRegionConfig.aidl
index a3086c6..f4c6fd3 100644
--- a/broadcastradio/aidl/android/hardware/broadcastradio/AmFmRegionConfig.aidl
+++ b/broadcastradio/aidl/android/hardware/broadcastradio/AmFmRegionConfig.aidl
@@ -73,14 +73,15 @@
/**
* De-emphasis filter supported/configured.
*
- * It is a bitset of de-emphasis values (DEEMPHASIS_D50 and DEEMPHASIS_D75).
+ * It can be a combination of de-emphasis values ({@link #DEEMPHASIS_D50} and
+ * {@link #DEEMPHASIS_D75}).
*/
int fmDeemphasis;
/**
* RDS/RBDS variant supported/configured.
*
- * It is a bitset of RDS values (RDS and RBDS).
+ * It can be a combination of RDS values ({@link #RDS} and {@link #RBDS}).
*/
int fmRds;
}
diff --git a/broadcastradio/aidl/android/hardware/broadcastradio/ProgramInfo.aidl b/broadcastradio/aidl/android/hardware/broadcastradio/ProgramInfo.aidl
index d650239..7632c81 100644
--- a/broadcastradio/aidl/android/hardware/broadcastradio/ProgramInfo.aidl
+++ b/broadcastradio/aidl/android/hardware/broadcastradio/ProgramInfo.aidl
@@ -150,6 +150,10 @@
/**
* Program flags.
+ *
+ * It can be a combination of {@link #FLAG_LIVE}, {@link #FLAG_MUTED},
+ * {@link #FLAG_TRAFFIC_PROGRAM}, {@link #FLAG_TRAFFIC_ANNOUNCEMENT},
+ * {@link #FLAG_TUNABLE}, and {@link #FLAG_STEREO}.
*/
int infoFlags;
diff --git a/broadcastradio/aidl/android/hardware/broadcastradio/ProgramSelector.aidl b/broadcastradio/aidl/android/hardware/broadcastradio/ProgramSelector.aidl
index 93b0e12..71b824b 100644
--- a/broadcastradio/aidl/android/hardware/broadcastradio/ProgramSelector.aidl
+++ b/broadcastradio/aidl/android/hardware/broadcastradio/ProgramSelector.aidl
@@ -51,8 +51,7 @@
* - analogue AM/FM: AMFM_FREQUENCY_KHZ;
* - FM RDS: RDS_PI;
* - HD Radio: HD_STATION_ID_EXT;
- * - DAB/DMB: DAB_SID_EXT (when used, DAB_ENSEMBLE and DAB_FREQUENCY_KHZ
- * must present in secondaryIds);
+ * - DAB/DMB: DAB_SID_EXT;
* - Digital Radio Mondiale: DRMO_SERVICE_ID;
* - SiriusXM: SXM_SERVICE_ID;
* - vendor-specific: VENDOR_START..VENDOR_END.
@@ -63,13 +62,16 @@
* Secondary program identifiers.
*
* These identifiers are supplementary and can speed up tuning process,
- * but the primary ID must be sufficient (i.e. RDS PI is enough to select
+ * but the primary ID should be sufficient (i.e. RDS PI is enough to select
* a station from the list after a full band scan).
*
* Two selectors with different secondary IDs, but the same primary ID are
* considered equal. In particular, secondary IDs array may get updated for
* an entry on the program list (ie. when a better frequency for a given
* station is found).
+ *
+ * If DAB_SID_EXT is used as primaryId, using DAB_ENSEMBLE or DAB_FREQUENCY_KHZ
+ * as secondray identifiers can uniquely identify the DAB station.
*/
ProgramIdentifier[] secondaryIds;
}
diff --git a/broadcastradio/aidl/default/BroadcastRadio.cpp b/broadcastradio/aidl/default/BroadcastRadio.cpp
index 36520fb..c0c475a 100644
--- a/broadcastradio/aidl/default/BroadcastRadio.cpp
+++ b/broadcastradio/aidl/default/BroadcastRadio.cpp
@@ -589,10 +589,11 @@
}
ProgramSelector sel = {};
if (isDab) {
- if (numArgs != 5) {
+ if (numArgs != 5 && numArgs != 3) {
dprintf(fd,
"Invalid number of arguments: please provide "
- "--tune dab <SID> <ENSEMBLE> <FREQUENCY>\n");
+ "--tune dab <SID> <ENSEMBLE> <FREQUENCY> or "
+ "--tune dab <SID>\n");
return STATUS_BAD_VALUE;
}
int sid;
@@ -600,17 +601,21 @@
dprintf(fd, "Non-integer sid provided with tune: %s\n", args[2]);
return STATUS_BAD_VALUE;
}
- int ensemble;
- if (!utils::parseArgInt(string(args[3]), &ensemble)) {
- dprintf(fd, "Non-integer ensemble provided with tune: %s\n", args[3]);
- return STATUS_BAD_VALUE;
+ if (numArgs == 3) {
+ sel = utils::makeSelectorDab(sid);
+ } else {
+ int ensemble;
+ if (!utils::parseArgInt(string(args[3]), &ensemble)) {
+ dprintf(fd, "Non-integer ensemble provided with tune: %s\n", args[3]);
+ return STATUS_BAD_VALUE;
+ }
+ int freq;
+ if (!utils::parseArgInt(string(args[4]), &freq)) {
+ dprintf(fd, "Non-integer frequency provided with tune: %s\n", args[4]);
+ return STATUS_BAD_VALUE;
+ }
+ sel = utils::makeSelectorDab(sid, ensemble, freq);
}
- int freq;
- if (!utils::parseArgInt(string(args[4]), &freq)) {
- dprintf(fd, "Non-integer frequency provided with tune: %s\n", args[4]);
- return STATUS_BAD_VALUE;
- }
- sel = utils::makeSelectorDab(sid, ensemble, freq);
} else {
if (numArgs != 3) {
dprintf(fd, "Invalid number of arguments: please provide --tune amfm <FREQUENCY>\n");
diff --git a/broadcastradio/common/utilsaidl/Utils.cpp b/broadcastradio/common/utilsaidl/Utils.cpp
index ad82366..0551bad 100644
--- a/broadcastradio/common/utilsaidl/Utils.cpp
+++ b/broadcastradio/common/utilsaidl/Utils.cpp
@@ -136,9 +136,18 @@
return getHdSubchannel(b) == 0 &&
haveEqualIds(a, b, IdentifierType::AMFM_FREQUENCY_KHZ);
case IdentifierType::DAB_SID_EXT:
- return haveEqualIds(a, b, IdentifierType::DAB_SID_EXT) &&
- haveEqualIds(a, b, IdentifierType::DAB_ENSEMBLE) &&
- haveEqualIds(a, b, IdentifierType::DAB_FREQUENCY_KHZ);
+ if (!haveEqualIds(a, b, IdentifierType::DAB_SID_EXT)) {
+ return false;
+ }
+ if (hasId(a, IdentifierType::DAB_ENSEMBLE) &&
+ !haveEqualIds(a, b, IdentifierType::DAB_ENSEMBLE)) {
+ return false;
+ }
+ if (hasId(a, IdentifierType::DAB_FREQUENCY_KHZ) &&
+ !haveEqualIds(a, b, IdentifierType::DAB_FREQUENCY_KHZ)) {
+ return false;
+ }
+ return true;
case IdentifierType::DRMO_SERVICE_ID:
return haveEqualIds(a, b, IdentifierType::DRMO_SERVICE_ID);
case IdentifierType::SXM_SERVICE_ID:
@@ -289,25 +298,7 @@
sel.primaryId.type > IdentifierType::VENDOR_END)) {
return false;
}
- if (!isValid(sel.primaryId)) {
- return false;
- }
-
- bool isDab = sel.primaryId.type == IdentifierType::DAB_SID_EXT;
- bool hasDabEnsemble = false;
- bool hasDabFrequency = false;
- for (auto it = sel.secondaryIds.begin(); it != sel.secondaryIds.end(); it++) {
- if (!isValid(*it)) {
- return false;
- }
- if (isDab && it->type == IdentifierType::DAB_ENSEMBLE) {
- hasDabEnsemble = true;
- }
- if (isDab && it->type == IdentifierType::DAB_FREQUENCY_KHZ) {
- hasDabFrequency = true;
- }
- }
- return !isDab || (hasDabEnsemble && hasDabFrequency);
+ return isValid(sel.primaryId);
}
ProgramIdentifier makeIdentifier(IdentifierType type, int64_t value) {
@@ -320,6 +311,12 @@
return sel;
}
+ProgramSelector makeSelectorDab(int64_t sidExt) {
+ ProgramSelector sel = {};
+ sel.primaryId = makeIdentifier(IdentifierType::DAB_SID_EXT, sidExt);
+ return sel;
+}
+
ProgramSelector makeSelectorDab(int64_t sidExt, int32_t ensemble, int64_t freq) {
ProgramSelector sel = {};
sel.primaryId = makeIdentifier(IdentifierType::DAB_SID_EXT, sidExt);
diff --git a/broadcastradio/common/utilsaidl/include/broadcastradio-utils-aidl/Utils.h b/broadcastradio/common/utilsaidl/include/broadcastradio-utils-aidl/Utils.h
index beebd03..ad075f2 100644
--- a/broadcastradio/common/utilsaidl/include/broadcastradio-utils-aidl/Utils.h
+++ b/broadcastradio/common/utilsaidl/include/broadcastradio-utils-aidl/Utils.h
@@ -138,6 +138,7 @@
ProgramIdentifier makeIdentifier(IdentifierType type, int64_t value);
ProgramSelector makeSelectorAmfm(int32_t frequency);
+ProgramSelector makeSelectorDab(int64_t sidExt);
ProgramSelector makeSelectorDab(int64_t sidExt, int32_t ensemble, int64_t freq);
bool satisfies(const ProgramFilter& filter, const ProgramSelector& sel);
diff --git a/camera/provider/aidl/vts/VtsAidlHalCameraProvider_TargetTest.cpp b/camera/provider/aidl/vts/VtsAidlHalCameraProvider_TargetTest.cpp
index 50fb052..a755d57 100644
--- a/camera/provider/aidl/vts/VtsAidlHalCameraProvider_TargetTest.cpp
+++ b/camera/provider/aidl/vts/VtsAidlHalCameraProvider_TargetTest.cpp
@@ -2025,16 +2025,7 @@
}
TEST_P(CameraAidlTest, process8BitColorSpaceRequests) {
- static int profiles[] = {
- ColorSpaceNamed::BT709,
- ColorSpaceNamed::DCI_P3,
- ColorSpaceNamed::DISPLAY_P3,
- ColorSpaceNamed::EXTENDED_SRGB,
- ColorSpaceNamed::LINEAR_EXTENDED_SRGB,
- ColorSpaceNamed::NTSC_1953,
- ColorSpaceNamed::SMPTE_C,
- ColorSpaceNamed::SRGB
- };
+ static int profiles[] = {ColorSpaceNamed::DISPLAY_P3, ColorSpaceNamed::SRGB};
for (int32_t i = 0; i < sizeof(profiles) / sizeof(profiles[0]); i++) {
processColorSpaceRequest(static_cast<RequestAvailableColorSpaceProfilesMap>(profiles[i]),
@@ -2059,10 +2050,10 @@
ANDROID_REQUEST_AVAILABLE_DYNAMIC_RANGE_PROFILES_MAP_DOLBY_VISION_8B_HDR_OEM_PO
};
- // Process all dynamic range profiles with BT2020
+ // Process all dynamic range profiles with BT2020_HLG
for (int32_t i = 0; i < sizeof(dynamicRangeProfiles) / sizeof(dynamicRangeProfiles[0]); i++) {
processColorSpaceRequest(
- static_cast<RequestAvailableColorSpaceProfilesMap>(ColorSpaceNamed::BT2020),
+ static_cast<RequestAvailableColorSpaceProfilesMap>(ColorSpaceNamed::BT2020_HLG),
static_cast<RequestAvailableDynamicRangeProfilesMap>(dynamicRangeProfiles[i]));
}
}
@@ -3051,6 +3042,47 @@
configureStreamUseCaseInternal(previewStreamThreshold);
}
+// Validate the integrity of stream configuration metadata
+TEST_P(CameraAidlTest, validateStreamConfigurations) {
+ std::vector<std::string> cameraDeviceNames = getCameraDeviceNames(mProvider);
+ std::vector<AvailableStream> outputStreams;
+
+ const int32_t scalerSizesTag = ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS;
+ const int32_t scalerMinFrameDurationsTag = ANDROID_SCALER_AVAILABLE_MIN_FRAME_DURATIONS;
+ const int32_t scalerStallDurationsTag = ANDROID_SCALER_AVAILABLE_STALL_DURATIONS;
+
+ for (const auto& name : cameraDeviceNames) {
+ CameraMetadata meta;
+ std::shared_ptr<ICameraDevice> cameraDevice;
+
+ openEmptyDeviceSession(name, mProvider, &mSession /*out*/, &meta /*out*/,
+ &cameraDevice /*out*/);
+ camera_metadata_t* staticMeta = reinterpret_cast<camera_metadata_t*>(meta.metadata.data());
+
+ if (is10BitDynamicRangeCapable(staticMeta)) {
+ std::vector<std::tuple<size_t, size_t>> supportedP010Sizes, supportedBlobSizes;
+
+ getSupportedSizes(staticMeta, scalerSizesTag, HAL_PIXEL_FORMAT_BLOB,
+ &supportedBlobSizes);
+ getSupportedSizes(staticMeta, scalerSizesTag, HAL_PIXEL_FORMAT_YCBCR_P010,
+ &supportedP010Sizes);
+ ASSERT_FALSE(supportedP010Sizes.empty());
+
+ std::vector<int64_t> blobMinDurations, blobStallDurations;
+ getSupportedDurations(staticMeta, scalerMinFrameDurationsTag, HAL_PIXEL_FORMAT_BLOB,
+ supportedP010Sizes, &blobMinDurations);
+ getSupportedDurations(staticMeta, scalerStallDurationsTag, HAL_PIXEL_FORMAT_BLOB,
+ supportedP010Sizes, &blobStallDurations);
+ ASSERT_FALSE(blobStallDurations.empty());
+ ASSERT_FALSE(blobMinDurations.empty());
+ ASSERT_EQ(supportedP010Sizes.size(), blobMinDurations.size());
+ ASSERT_EQ(blobMinDurations.size(), blobStallDurations.size());
+ }
+
+ // Validate other aspects of stream configuration metadata...
+ }
+}
+
GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(CameraAidlTest);
INSTANTIATE_TEST_SUITE_P(
PerInstance, CameraAidlTest,
diff --git a/camera/provider/aidl/vts/camera_aidl_test.cpp b/camera/provider/aidl/vts/camera_aidl_test.cpp
index 64507fe..c315879 100644
--- a/camera/provider/aidl/vts/camera_aidl_test.cpp
+++ b/camera/provider/aidl/vts/camera_aidl_test.cpp
@@ -3085,6 +3085,10 @@
return "CIE_XYZ";
case ColorSpaceNamed::CIE_LAB:
return "CIE_LAB";
+ case ColorSpaceNamed::BT2020_HLG:
+ return "BT2020_HLG";
+ case ColorSpaceNamed::BT2020_PQ:
+ return "BT2020_PQ";
default:
return "INVALID";
}
@@ -3333,7 +3337,6 @@
RequestAvailableColorSpaceProfilesMap colorSpace,
RequestAvailableDynamicRangeProfilesMap dynamicRangeProfile) {
std::vector<std::string> cameraDeviceNames = getCameraDeviceNames(mProvider);
- int64_t bufferId = 1;
CameraMetadata settings;
for (const auto& name : cameraDeviceNames) {
@@ -3452,12 +3455,12 @@
// Stream as long as needed to fill the Hal inflight queue
std::vector<CaptureRequest> requests(halStreams[0].maxBuffers);
- for (int32_t frameNumber = 0; frameNumber < requests.size(); frameNumber++) {
+ for (int32_t requestId = 0; requestId < requests.size(); requestId++) {
std::shared_ptr<InFlightRequest> inflightReq = std::make_shared<InFlightRequest>(
static_cast<ssize_t>(halStreams.size()), false, supportsPartialResults,
partialResultCount, std::unordered_set<std::string>(), resultQueue);
- CaptureRequest& request = requests[frameNumber];
+ CaptureRequest& request = requests[requestId];
std::vector<StreamBuffer>& outputBuffers = request.outputBuffers;
outputBuffers.resize(halStreams.size());
@@ -3466,6 +3469,7 @@
std::vector<buffer_handle_t> graphicBuffers;
graphicBuffers.reserve(halStreams.size());
+ auto bufferId = requestId + 1; // Buffer id value 0 is not valid
for (const auto& halStream : halStreams) {
buffer_handle_t buffer_handle;
if (useHalBufManager) {
@@ -3481,17 +3485,16 @@
inflightReq->mOutstandingBufferIds[halStream.id][bufferId] = buffer_handle;
graphicBuffers.push_back(buffer_handle);
- outputBuffers[k] = {halStream.id, bufferId,
- android::makeToAidl(buffer_handle), BufferStatus::OK, NativeHandle(),
- NativeHandle()};
- bufferId++;
+ outputBuffers[k] = {
+ halStream.id, bufferId, android::makeToAidl(buffer_handle),
+ BufferStatus::OK, NativeHandle(), NativeHandle()};
}
k++;
}
request.inputBuffer = {
-1, 0, NativeHandle(), BufferStatus::ERROR, NativeHandle(), NativeHandle()};
- request.frameNumber = frameNumber;
+ request.frameNumber = bufferId;
request.fmqSettingsSize = 0;
request.settings = settings;
request.inputWidth = 0;
@@ -3499,9 +3502,8 @@
{
std::unique_lock<std::mutex> l(mLock);
- mInflightMap[frameNumber] = inflightReq;
+ mInflightMap[bufferId] = inflightReq;
}
-
}
int32_t numRequestProcessed = 0;
@@ -3515,7 +3517,10 @@
std::vector<int32_t> {halStreams[0].id});
ASSERT_TRUE(returnStatus.isOk());
- for (int32_t frameNumber = 0; frameNumber < requests.size(); frameNumber++) {
+ // We are keeping frame numbers and buffer ids consistent. Buffer id value of 0
+ // is used to indicate a buffer that is not present/available so buffer ids as well
+ // as frame numbers begin with 1.
+ for (int32_t frameNumber = 1; frameNumber <= requests.size(); frameNumber++) {
const auto& inflightReq = mInflightMap[frameNumber];
std::unique_lock<std::mutex> l(mLock);
while (!inflightReq->errorCodeValid &&
@@ -3703,3 +3708,48 @@
ASSERT_TRUE(ret.isOk());
}
}
+
+void CameraAidlTest::getSupportedSizes(const camera_metadata_t* ch, uint32_t tag, int32_t format,
+ std::vector<std::tuple<size_t, size_t>>* sizes /*out*/) {
+ if (sizes == nullptr) {
+ return;
+ }
+
+ camera_metadata_ro_entry entry;
+ int retcode = find_camera_metadata_ro_entry(ch, tag, &entry);
+ if ((0 == retcode) && (entry.count > 0)) {
+ // Scaler entry contains 4 elements (format, width, height, type)
+ for (size_t i = 0; i < entry.count; i += 4) {
+ if ((entry.data.i32[i] == format) &&
+ (entry.data.i32[i + 3] == ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS_OUTPUT)) {
+ sizes->push_back(std::make_tuple(entry.data.i32[i + 1], entry.data.i32[i + 2]));
+ }
+ }
+ }
+}
+
+void CameraAidlTest::getSupportedDurations(const camera_metadata_t* ch, uint32_t tag,
+ int32_t format,
+ const std::vector<std::tuple<size_t, size_t>>& sizes,
+ std::vector<int64_t>* durations /*out*/) {
+ if (durations == nullptr) {
+ return;
+ }
+
+ camera_metadata_ro_entry entry;
+ int retcode = find_camera_metadata_ro_entry(ch, tag, &entry);
+ if ((0 == retcode) && (entry.count > 0)) {
+ // Duration entry contains 4 elements (format, width, height, duration)
+ for (const auto& size : sizes) {
+ int64_t width = std::get<0>(size);
+ int64_t height = std::get<1>(size);
+ for (size_t i = 0; i < entry.count; i += 4) {
+ if ((entry.data.i64[i] == format) && (entry.data.i64[i + 1] == width) &&
+ (entry.data.i64[i + 2] == height)) {
+ durations->push_back(entry.data.i64[i + 3]);
+ break;
+ }
+ }
+ }
+ }
+}
diff --git a/camera/provider/aidl/vts/camera_aidl_test.h b/camera/provider/aidl/vts/camera_aidl_test.h
index f13d6b2..4b4c039 100644
--- a/camera/provider/aidl/vts/camera_aidl_test.h
+++ b/camera/provider/aidl/vts/camera_aidl_test.h
@@ -145,7 +145,9 @@
ACES,
ACESCG,
CIE_XYZ,
- CIE_LAB
+ CIE_LAB,
+ BT2020_HLG,
+ BT2020_PQ
};
struct AvailableZSLInputOutput {
@@ -418,6 +420,13 @@
bool supportsCroppedRawUseCase(const camera_metadata_t *staticMeta);
bool isPerFrameControl(const camera_metadata_t* staticMeta);
+ void getSupportedSizes(const camera_metadata_t* ch, uint32_t tag, int32_t format,
+ std::vector<std::tuple<size_t, size_t>>* sizes /*out*/);
+
+ void getSupportedDurations(const camera_metadata_t* ch, uint32_t tag, int32_t format,
+ const std::vector<std::tuple<size_t, size_t>>& sizes,
+ std::vector<int64_t>* durations /*out*/);
+
protected:
// In-flight queue for tracking completion of capture requests.
struct InFlightRequest {
diff --git a/common/fmq/aidl/Android.bp b/common/fmq/aidl/Android.bp
index 5772b7f..3b022fc 100644
--- a/common/fmq/aidl/Android.bp
+++ b/common/fmq/aidl/Android.bp
@@ -48,5 +48,10 @@
},
},
frozen: true,
- versions: ["1"],
+ versions_with_info: [
+ {
+ version: "1",
+ imports: ["android.hardware.common-V2"],
+ },
+ ],
}
diff --git a/compatibility_matrices/compatibility_matrix.4.xml b/compatibility_matrices/compatibility_matrix.4.xml
index c898aab..b9fb3f4 100644
--- a/compatibility_matrices/compatibility_matrix.4.xml
+++ b/compatibility_matrices/compatibility_matrix.4.xml
@@ -7,7 +7,7 @@
<instance>default</instance>
</interface>
</hal>
- <hal format="hidl" optional="false">
+ <hal format="hidl" optional="true">
<name>android.hardware.audio</name>
<version>5.0</version>
<interface>
@@ -15,7 +15,7 @@
<instance>default</instance>
</interface>
</hal>
- <hal format="hidl" optional="false">
+ <hal format="hidl" optional="true">
<name>android.hardware.audio.effect</name>
<version>5.0</version>
<interface>
@@ -199,7 +199,7 @@
<instance>default</instance>
</interface>
</hal>
- <hal format="hidl" optional="false">
+ <hal format="hidl" optional="true">
<name>android.hardware.graphics.composer</name>
<version>2.1-3</version>
<interface>
@@ -207,7 +207,7 @@
<instance>default</instance>
</interface>
</hal>
- <hal format="hidl" optional="false">
+ <hal format="hidl" optional="true">
<name>android.hardware.graphics.mapper</name>
<version>2.1</version>
<version>3.0</version>
diff --git a/compatibility_matrices/compatibility_matrix.5.xml b/compatibility_matrices/compatibility_matrix.5.xml
index c5a1dc2..b374c8c 100644
--- a/compatibility_matrices/compatibility_matrix.5.xml
+++ b/compatibility_matrices/compatibility_matrix.5.xml
@@ -7,7 +7,7 @@
<instance>default</instance>
</interface>
</hal>
- <hal format="hidl" optional="false">
+ <hal format="hidl" optional="true">
<name>android.hardware.audio</name>
<version>6.0</version>
<interface>
@@ -15,7 +15,7 @@
<instance>default</instance>
</interface>
</hal>
- <hal format="hidl" optional="false">
+ <hal format="hidl" optional="true">
<name>android.hardware.audio.effect</name>
<version>6.0</version>
<interface>
@@ -222,7 +222,7 @@
<instance>default</instance>
</interface>
</hal>
- <hal format="hidl" optional="false">
+ <hal format="hidl" optional="true">
<name>android.hardware.graphics.composer</name>
<version>2.1-4</version>
<interface>
@@ -230,7 +230,7 @@
<instance>default</instance>
</interface>
</hal>
- <hal format="hidl" optional="false">
+ <hal format="hidl" optional="true">
<name>android.hardware.graphics.mapper</name>
<!-- New, non-Go devices should use 4.0, tested in vts_treble_vintf_vendor_test -->
<version>2.1</version>
@@ -367,7 +367,7 @@
<instance>default</instance>
</interface>
</hal>
- <hal format="aidl" optional="false">
+ <hal format="aidl" optional="true">
<name>android.hardware.power</name>
<interface>
<name>IPower</name>
diff --git a/compatibility_matrices/compatibility_matrix.6.xml b/compatibility_matrices/compatibility_matrix.6.xml
index 1c76ee6..40ae655 100644
--- a/compatibility_matrices/compatibility_matrix.6.xml
+++ b/compatibility_matrices/compatibility_matrix.6.xml
@@ -7,7 +7,7 @@
<instance>default</instance>
</interface>
</hal>
- <hal format="hidl" optional="false">
+ <hal format="hidl" optional="true">
<name>android.hardware.audio</name>
<version>6.0</version>
<version>7.0</version>
@@ -16,7 +16,7 @@
<instance>default</instance>
</interface>
</hal>
- <hal format="hidl" optional="false">
+ <hal format="hidl" optional="true">
<name>android.hardware.audio.effect</name>
<version>6.0</version>
<version>7.0</version>
@@ -252,7 +252,7 @@
<instance>default</instance>
</interface>
</hal>
- <hal format="hidl" optional="false">
+ <hal format="hidl" optional="true">
<name>android.hardware.graphics.composer</name>
<version>2.1-4</version>
<interface>
@@ -260,7 +260,7 @@
<instance>default</instance>
</interface>
</hal>
- <hal format="hidl" optional="false">
+ <hal format="hidl" optional="true">
<name>android.hardware.graphics.mapper</name>
<!-- New, non-Go devices should use 4.0, tested in vts_treble_vintf_vendor_test -->
<version>2.1</version>
@@ -423,7 +423,7 @@
<instance>default</instance>
</interface>
</hal>
- <hal format="aidl" optional="false">
+ <hal format="aidl" optional="true">
<name>android.hardware.power</name>
<version>1-2</version>
<interface>
diff --git a/compatibility_matrices/compatibility_matrix.7.xml b/compatibility_matrices/compatibility_matrix.7.xml
index 67dd717..e5ef954 100644
--- a/compatibility_matrices/compatibility_matrix.7.xml
+++ b/compatibility_matrices/compatibility_matrix.7.xml
@@ -327,7 +327,7 @@
<instance>default</instance>
</interface>
</hal>
- <hal format="aidl" optional="false">
+ <hal format="aidl" optional="true">
<name>android.hardware.health</name>
<version>1</version>
<interface>
@@ -493,7 +493,7 @@
<instance>default</instance>
</interface>
</hal>
- <hal format="aidl" optional="false">
+ <hal format="aidl" optional="true">
<name>android.hardware.power</name>
<version>2-3</version>
<interface>
diff --git a/compatibility_matrices/compatibility_matrix.8.xml b/compatibility_matrices/compatibility_matrix.8.xml
index c58f559..310eaa1 100644
--- a/compatibility_matrices/compatibility_matrix.8.xml
+++ b/compatibility_matrices/compatibility_matrix.8.xml
@@ -1,5 +1,5 @@
<compatibility-matrix version="1.0" type="framework" level="8">
- <hal format="hidl" optional="false">
+ <hal format="hidl" optional="true">
<name>android.hardware.audio</name>
<version>6.0</version>
<version>7.0-1</version>
@@ -62,7 +62,7 @@
</hal>
<hal format="aidl" optional="true">
<name>android.hardware.automotive.audiocontrol</name>
- <version>2</version>
+ <version>2-3</version>
<interface>
<name>IAudioControl</name>
<instance>default</instance>
@@ -84,15 +84,6 @@
<regex-instance>[a-z]+/[0-9]+</regex-instance>
</interface>
</hal>
- <hal format="hidl" optional="true">
- <name>android.hardware.automotive.evs</name>
- <version>1.0-1</version>
- <interface>
- <name>IEvsEnumerator</name>
- <instance>default</instance>
- <regex-instance>[a-z]+/[0-9]+</regex-instance>
- </interface>
- </hal>
<hal format="aidl" optional="true">
<name>android.hardware.automotive.occupant_awareness</name>
<version>1</version>
@@ -103,6 +94,7 @@
</hal>
<hal format="aidl" optional="true">
<name>android.hardware.automotive.vehicle</name>
+ <version>1-2</version>
<interface>
<name>IVehicle</name>
<instance>default</instance>
@@ -282,7 +274,7 @@
<instance>default</instance>
</interface>
</hal>
- <hal format="aidl" optional="false">
+ <hal format="aidl" optional="true">
<name>android.hardware.health</name>
<version>1-2</version>
<interface>
@@ -395,7 +387,7 @@
<instance>default</instance>
</interface>
</hal>
- <hal format="aidl" optional="false">
+ <hal format="aidl" optional="true">
<name>android.hardware.power</name>
<version>4</version>
<interface>
diff --git a/current.txt b/current.txt
index a6c4d80..754093f 100644
--- a/current.txt
+++ b/current.txt
@@ -937,5 +937,6 @@
889b59e3e7a59afa67bf19882a44f51a2f9e43b6556ec52baa9ec3efd1ef7fbe android.hardware.camera.device@3.2::types
db37a1c757e2e69b1ec9c75a981a6987bd87a131d92ab6acc00e04d19f374281 android.hardware.automotive.vehicle@2.0::types
997017f581406fca1675d2f612f7ccd73f0d04eadd54bf6212e6cf5971d0872d android.hardware.automotive.vehicle@2.0::types
+06983ffe6d75e90a22503a6d9fd14417f983a36bb060a80ad5915240d69b8a40 android.hardware.automotive.vehicle@2.0::types
# There will be no more HIDL HALs. Use AIDL instead.
diff --git a/drm/aidl/Android.bp b/drm/aidl/Android.bp
index 6273a38..fb04d84 100644
--- a/drm/aidl/Android.bp
+++ b/drm/aidl/Android.bp
@@ -23,7 +23,7 @@
sdk_version: "module_current",
},
ndk: {
- min_sdk_version: "current",
+ min_sdk_version: "UpsideDownCake",
},
},
double_loadable: true,
diff --git a/drm/aidl/OWNERS b/drm/aidl/OWNERS
index fa8fd20..fe69725 100644
--- a/drm/aidl/OWNERS
+++ b/drm/aidl/OWNERS
@@ -1,4 +1,3 @@
# Bug component: 49079
-edwinwong@google.com
kelzhan@google.com
robertshih@google.com
diff --git a/drm/aidl/vts/OWNERS b/drm/aidl/vts/OWNERS
index e44b93e..fe69725 100644
--- a/drm/aidl/vts/OWNERS
+++ b/drm/aidl/vts/OWNERS
@@ -1,4 +1,3 @@
-edwinwong@google.com
-jtinker@google.com
+# Bug component: 49079
kelzhan@google.com
robertshih@google.com
diff --git a/gnss/aidl/vts/gnss_hal_test_cases.cpp b/gnss/aidl/vts/gnss_hal_test_cases.cpp
index 17d65f5..aa8bdfd 100644
--- a/gnss/aidl/vts/gnss_hal_test_cases.cpp
+++ b/gnss/aidl/vts/gnss_hal_test_cases.cpp
@@ -1132,8 +1132,10 @@
status = iAGnssRil->setRefLocation(agnssReflocation);
ASSERT_TRUE(status.isOk());
- status = iAGnssRil->injectNiSuplMessageData(std::vector<uint8_t>(), 0);
- ASSERT_FALSE(status.isOk());
+ if (aidl_gnss_hal_->getInterfaceVersion() >= 3) {
+ status = iAGnssRil->injectNiSuplMessageData(std::vector<uint8_t>(), 0);
+ ASSERT_FALSE(status.isOk());
+ }
}
/*
diff --git a/graphics/common/aidl/android/hardware/graphics/common/Dataspace.aidl b/graphics/common/aidl/android/hardware/graphics/common/Dataspace.aidl
index b44e613..4f2a087 100644
--- a/graphics/common/aidl/android/hardware/graphics/common/Dataspace.aidl
+++ b/graphics/common/aidl/android/hardware/graphics/common/Dataspace.aidl
@@ -87,7 +87,7 @@
* Use the unadjusted KR = 0.2126, KB = 0.0722 luminance interpretation
* for RGB conversion.
*/
- STANDARD_BT709 = 1 << 16, // 1 << STANDARD_SHIFT
+ STANDARD_BT709 = 1 << 16, // 1 << STANDARD_SHIFT
/**
* Primaries: x y
@@ -377,11 +377,19 @@
RANGE_LIMITED = 2 << 27, // 2 << RANGE_SHIFT = 0x10000000
/**
- * Extended range is used for scRGB. Intended for use with
- * floating point pixel formats. [0.0 - 1.0] is the standard
- * sRGB space. Values outside the range 0.0 - 1.0 can encode
- * color outside the sRGB gamut.
- * Used to blend / merge multiple dataspaces on a single display.
+ * Extended range can be used in combination with FP16 to communicate scRGB or with
+ * SurfaceControl's setExtendedRangeBrightness(SurfaceControl, float, float)
+ * to indicate an HDR range.
+ *
+ * When used with floating point pixel formats and #STANDARD_BT709 then [0.0 - 1.0] is the
+ * standard sRGB space and values outside the range [0.0 - 1.0] can encode
+ * color outside the sRGB gamut. [-0.5, 7.5] is the standard scRGB range.
+ * Used to blend/merge multiple dataspaces on a single display.
+ *
+ * As of Android U in combination with composer3's mixed SDR/HDR feature then this may
+ * be combined with SurfaceControl's setExtendedRangeBrightness(SurfaceControl, float, float)
+ * and other formats such as RGBA_8888 or RGBA_1010102 to communicate a variable HDR
+ * brightness range, which in turn will influence that layer's dimming ratio when composited
*/
RANGE_EXTENDED = 3 << 27, // 3 << RANGE_SHIFT = 0x18000000
@@ -397,7 +405,6 @@
*/
SRGB_LINEAR = 1 << 16 | 1 << 22 | 1 << 27, // STANDARD_BT709 | TRANSFER_LINEAR | RANGE_FULL
-
/**
* scRGB linear encoding:
*
@@ -412,7 +419,6 @@
*/
SCRGB_LINEAR = 1 << 16 | 1 << 22 | 3 << 27, // STANDARD_BT709 | TRANSFER_LINEAR | RANGE_EXTENDED
-
/**
* sRGB gamma encoding:
*
@@ -428,7 +434,6 @@
*/
SRGB = 1 << 16 | 2 << 22 | 1 << 27, // STANDARD_BT709 | TRANSFER_SRGB | RANGE_FULL
-
/**
* scRGB:
*
@@ -470,8 +475,8 @@
*
* Use limited range, SMPTE 170M transfer and BT.601_625 standard.
*/
- BT601_625 = 2 << 16 | 3 << 22 | 2 << 27, // STANDARD_BT601_625 | TRANSFER_SMPTE_170M | RANGE_LIMITED
-
+ BT601_625 =
+ 2 << 16 | 3 << 22 | 2 << 27, // STANDARD_BT601_625 | TRANSFER_SMPTE_170M | RANGE_LIMITED
/**
* ITU-R Recommendation 601 (BT.601) - 525-line
@@ -480,7 +485,8 @@
*
* Use limited range, SMPTE 170M transfer and BT.601_525 standard.
*/
- BT601_525 = 4 << 16 | 3 << 22 | 2 << 27, // STANDARD_BT601_525 | TRANSFER_SMPTE_170M | RANGE_LIMITED
+ BT601_525 =
+ 4 << 16 | 3 << 22 | 2 << 27, // STANDARD_BT601_525 | TRANSFER_SMPTE_170M | RANGE_LIMITED
/**
* ITU-R Recommendation 709 (BT.709)
@@ -491,7 +497,6 @@
*/
BT709 = 1 << 16 | 3 << 22 | 2 << 27, // STANDARD_BT709 | TRANSFER_SMPTE_170M | RANGE_LIMITED
-
/**
* SMPTE EG 432-1 and SMPTE RP 431-2.
*
@@ -501,7 +506,6 @@
*/
DCI_P3_LINEAR = 10 << 16 | 1 << 22 | 1 << 27, // STANDARD_DCI_P3 | TRANSFER_LINEAR | RANGE_FULL
-
/**
* SMPTE EG 432-1 and SMPTE RP 431-2.
*
@@ -513,15 +517,14 @@
*/
DCI_P3 = 10 << 16 | 5 << 22 | 1 << 27, // STANDARD_DCI_P3 | TRANSFER_GAMMA2_6 | RANGE_FULL
-
/**
* Display P3
*
* Display P3 uses same primaries and white-point as DCI-P3
* linear transfer function makes this the same as DCI_P3_LINEAR.
*/
- DISPLAY_P3_LINEAR = 10 << 16 | 1 << 22 | 1 << 27, // STANDARD_DCI_P3 | TRANSFER_LINEAR | RANGE_FULL
-
+ DISPLAY_P3_LINEAR =
+ 10 << 16 | 1 << 22 | 1 << 27, // STANDARD_DCI_P3 | TRANSFER_LINEAR | RANGE_FULL
/**
* Display P3
@@ -531,7 +534,6 @@
*/
DISPLAY_P3 = 10 << 16 | 2 << 22 | 1 << 27, // STANDARD_DCI_P3 | TRANSFER_SRGB | RANGE_FULL
-
/**
* Adobe RGB
*
@@ -541,7 +543,6 @@
*/
ADOBE_RGB = 11 << 16 | 4 << 22 | 1 << 27, // STANDARD_ADOBE_RGB | TRANSFER_GAMMA2_2 | RANGE_FULL
-
/**
* ITU-R Recommendation 2020 (BT.2020)
*
@@ -551,7 +552,6 @@
*/
BT2020_LINEAR = 6 << 16 | 1 << 22 | 1 << 27, // STANDARD_BT2020 | TRANSFER_LINEAR | RANGE_FULL
-
/**
* ITU-R Recommendation 2020 (BT.2020)
*
@@ -570,7 +570,6 @@
*/
BT2020_PQ = 6 << 16 | 7 << 22 | 1 << 27, // STANDARD_BT2020 | TRANSFER_ST2084 | RANGE_FULL
-
/**
* Data spaces for non-color formats
*/
@@ -611,7 +610,8 @@
*
* Use limited range, SMPTE 170M transfer and BT2020 standard
*/
- BT2020_ITU = 6 << 16 | 3 << 22 | 2 << 27, // STANDARD_BT2020 | TRANSFER_SMPTE_170M | RANGE_LIMITED
+ BT2020_ITU =
+ 6 << 16 | 3 << 22 | 2 << 27, // STANDARD_BT2020 | TRANSFER_SMPTE_170M | RANGE_LIMITED
/**
* ITU-R Recommendation 2100 (BT.2100)
@@ -621,7 +621,8 @@
* Use limited/full range, PQ/HLG transfer, and BT2020 standard
* limited range is the preferred / normative definition for BT.2100
*/
- BT2020_ITU_PQ = 6 << 16 | 7 << 22 | 2 << 27, // STANDARD_BT2020 | TRANSFER_ST2084 | RANGE_LIMITED
+ BT2020_ITU_PQ =
+ 6 << 16 | 7 << 22 | 2 << 27, // STANDARD_BT2020 | TRANSFER_ST2084 | RANGE_LIMITED
BT2020_ITU_HLG = 6 << 16 | 8 << 22 | 2 << 27, // STANDARD_BT2020 | TRANSFER_HLG | RANGE_LIMITED
BT2020_HLG = 6 << 16 | 8 << 22 | 1 << 27, // STANDARD_BT2020 | TRANSFER_HLG | RANGE_FULL
@@ -687,5 +688,6 @@
*
* Use full range, SMPTE 170M transfer and BT.709 standard.
*/
- BT709_FULL_RANGE = 1 << 16 | 3 << 22 | 1 << 27, // STANDARD_BT709 | TRANSFER_SMPTE_170M | RANGE_FULL
+ BT709_FULL_RANGE =
+ 1 << 16 | 3 << 22 | 1 << 27, // STANDARD_BT709 | TRANSFER_SMPTE_170M | RANGE_FULL
}
diff --git a/graphics/composer/2.1/vts/functional/VtsHalGraphicsComposerV2_1TargetTest.cpp b/graphics/composer/2.1/vts/functional/VtsHalGraphicsComposerV2_1TargetTest.cpp
index 4822678..9444d89 100644
--- a/graphics/composer/2.1/vts/functional/VtsHalGraphicsComposerV2_1TargetTest.cpp
+++ b/graphics/composer/2.1/vts/functional/VtsHalGraphicsComposerV2_1TargetTest.cpp
@@ -666,11 +666,13 @@
ASSERT_NO_FATAL_FAILURE(GraphicsComposerHidlTest::TearDown());
}
- NativeHandleWrapper allocate() {
+ NativeHandleWrapper allocate() { return allocate(mDisplayWidth, mDisplayHeight); }
+
+ NativeHandleWrapper allocate(uint32_t width, uint32_t height) {
uint64_t usage =
static_cast<uint64_t>(BufferUsage::CPU_WRITE_OFTEN | BufferUsage::CPU_READ_OFTEN |
BufferUsage::COMPOSER_OVERLAY);
- return mGralloc->allocate(mDisplayWidth, mDisplayHeight, 1, PixelFormat::RGBA_8888, usage);
+ return mGralloc->allocate(width, height, 1, PixelFormat::RGBA_8888, usage);
}
void execute() { mComposerClient->execute(mReader.get(), mWriter.get()); }
@@ -884,6 +886,106 @@
}
/**
+ * Test IComposerClient::Command::SET_LAYER_BUFFER with the behavior used for clearing buffer slots.
+ */
+TEST_P(GraphicsComposerHidlCommandTest, SET_LAYER_BUFFER_multipleTimes) {
+ // A placeholder buffer used to clear buffer slots
+ auto clearSlotBuffer = allocate(1u, 1u);
+
+ //
+ // Set the layer buffer to the first buffer
+ //
+ auto handle1 = allocate();
+ ASSERT_NE(nullptr, handle1.get());
+ IComposerClient::Rect displayFrame{0, 0, mDisplayWidth, mDisplayHeight};
+ Layer layer;
+ ASSERT_NO_FATAL_FAILURE(
+ layer = mComposerClient->createLayer(mPrimaryDisplay, kBufferSlotCount));
+ mWriter->selectDisplay(mPrimaryDisplay);
+ mWriter->selectLayer(layer);
+ mWriter->setLayerCompositionType(IComposerClient::Composition::DEVICE);
+ mWriter->setLayerDisplayFrame(displayFrame);
+ mWriter->setLayerBuffer(0, handle1.get(), -1);
+ mWriter->setLayerDataspace(Dataspace::UNKNOWN);
+ mWriter->validateDisplay();
+ execute();
+ if (mReader->mCompositionChanges.size() != 0) {
+ GTEST_SUCCEED() << "Composition change requested, skipping test";
+ return;
+ }
+ ASSERT_EQ(0, mReader->mErrors.size());
+ mWriter->selectDisplay(mPrimaryDisplay);
+ mWriter->presentDisplay();
+ execute();
+ ASSERT_EQ(0, mReader->mErrors.size());
+
+ //
+ // Set the layer buffer to the second buffer
+ //
+ auto handle2 = allocate();
+ ASSERT_NE(nullptr, handle2.get());
+ mWriter->selectDisplay(mPrimaryDisplay);
+ mWriter->selectLayer(layer);
+ mWriter->setLayerCompositionType(IComposerClient::Composition::DEVICE);
+ mWriter->setLayerDisplayFrame(displayFrame);
+ mWriter->setLayerBuffer(1, handle2.get(), -1);
+ mWriter->setLayerDataspace(Dataspace::UNKNOWN);
+ mWriter->validateDisplay();
+ execute();
+ if (mReader->mCompositionChanges.size() != 0) {
+ GTEST_SUCCEED() << "Composition change requested, skipping test";
+ return;
+ }
+ ASSERT_EQ(0, mReader->mErrors.size());
+ mWriter->selectDisplay(mPrimaryDisplay);
+ mWriter->presentDisplay();
+ execute();
+ ASSERT_EQ(0, mReader->mErrors.size());
+
+ //
+ // Set the layer buffer to the third buffer
+ //
+ auto handle3 = allocate();
+ ASSERT_NE(nullptr, handle3.get());
+ mWriter->selectDisplay(mPrimaryDisplay);
+ mWriter->selectLayer(layer);
+ mWriter->setLayerCompositionType(IComposerClient::Composition::DEVICE);
+ mWriter->setLayerDisplayFrame(displayFrame);
+ mWriter->setLayerBuffer(2, handle3.get(), -1);
+ mWriter->setLayerDataspace(Dataspace::UNKNOWN);
+ mWriter->validateDisplay();
+ execute();
+ if (mReader->mCompositionChanges.size() != 0) {
+ GTEST_SUCCEED() << "Composition change requested, skipping test";
+ return;
+ }
+ ASSERT_EQ(0, mReader->mErrors.size());
+ mWriter->selectDisplay(mPrimaryDisplay);
+ mWriter->presentDisplay();
+ execute();
+ ASSERT_EQ(0, mReader->mErrors.size());
+
+ // Ensure we can clear multiple buffer slots and then restore the active buffer at the end
+ mWriter->selectDisplay(mPrimaryDisplay);
+ mWriter->selectLayer(layer);
+ mWriter->setLayerBuffer(0, clearSlotBuffer.get(), -1);
+ mWriter->selectDisplay(mPrimaryDisplay);
+ mWriter->selectLayer(layer);
+ mWriter->setLayerBuffer(1, clearSlotBuffer.get(), -1);
+ mWriter->selectDisplay(mPrimaryDisplay);
+ mWriter->selectLayer(layer);
+ mWriter->setLayerBuffer(2, nullptr, -1);
+ mWriter->validateDisplay();
+ execute();
+ ASSERT_EQ(0, mReader->mErrors.size());
+
+ mWriter->selectDisplay(mPrimaryDisplay);
+ mWriter->presentDisplay();
+ execute();
+ ASSERT_EQ(0, mReader->mErrors.size());
+}
+
+/**
* Test IComposerClient::Command::SET_LAYER_SURFACE_DAMAGE.
*/
TEST_P(GraphicsComposerHidlCommandTest, SET_LAYER_SURFACE_DAMAGE) {
diff --git a/graphics/composer/aidl/aidl_api/android.hardware.graphics.composer3/current/android/hardware/graphics/composer3/DisplayCapability.aidl b/graphics/composer/aidl/aidl_api/android.hardware.graphics.composer3/current/android/hardware/graphics/composer3/DisplayCapability.aidl
index 0e2d72b..6eba887 100644
--- a/graphics/composer/aidl/aidl_api/android.hardware.graphics.composer3/current/android/hardware/graphics/composer3/DisplayCapability.aidl
+++ b/graphics/composer/aidl/aidl_api/android.hardware.graphics.composer3/current/android/hardware/graphics/composer3/DisplayCapability.aidl
@@ -42,5 +42,4 @@
AUTO_LOW_LATENCY_MODE = 5,
SUSPEND = 6,
DISPLAY_IDLE_TIMER = 7,
- MULTI_THREADED_PRESENT = 8,
}
diff --git a/graphics/composer/aidl/android/hardware/graphics/composer3/DisplayCapability.aidl b/graphics/composer/aidl/android/hardware/graphics/composer3/DisplayCapability.aidl
index 7154d74..f4b2984 100644
--- a/graphics/composer/aidl/android/hardware/graphics/composer3/DisplayCapability.aidl
+++ b/graphics/composer/aidl/android/hardware/graphics/composer3/DisplayCapability.aidl
@@ -80,20 +80,4 @@
* IComposerCallback.onVsyncIdle.
*/
DISPLAY_IDLE_TIMER = 7,
- /**
- * Indicates that both the composer HAL implementation and the given display
- * support calling executeCommands concurrently from separate threads.
- * executeCommands for a particular display will never run concurrently to
- * any other executeCommands for the same display. In addition, the
- * CommandResultPayload must only reference displays included in the
- * DisplayCommands passed to executeCommands. Displays referenced from
- * separate threads must have minimal interference with one another. If a
- * HWC-managed display has this capability, SurfaceFlinger can run
- * executeCommands for this display concurrently with other displays with the
- * same capability.
- * @see IComposerClient.executeCommands
- * @see DisplayCommand.presentDisplay
- * @see DisplayCommand.validateDisplay
- */
- MULTI_THREADED_PRESENT = 8,
}
diff --git a/graphics/composer/aidl/vts/VtsHalGraphicsComposer3_TargetTest.cpp b/graphics/composer/aidl/vts/VtsHalGraphicsComposer3_TargetTest.cpp
index 746330b..3ea5bf5 100644
--- a/graphics/composer/aidl/vts/VtsHalGraphicsComposer3_TargetTest.cpp
+++ b/graphics/composer/aidl/vts/VtsHalGraphicsComposer3_TargetTest.cpp
@@ -118,6 +118,12 @@
[&](const Capability& activeCapability) { return activeCapability == capability; });
}
+ int getInterfaceVersion() {
+ const auto& [versionStatus, version] = mComposerClient->getInterfaceVersion();
+ EXPECT_TRUE(versionStatus.isOk());
+ return version;
+ }
+
const VtsDisplay& getPrimaryDisplay() const { return mDisplays[0]; }
int64_t getPrimaryDisplayId() const { return getPrimaryDisplay().getDisplayId(); }
@@ -874,9 +880,7 @@
}
TEST_P(GraphicsComposerAidlTest, GetOverlaySupport) {
- const auto& [versionStatus, version] = mComposerClient->getInterfaceVersion();
- ASSERT_TRUE(versionStatus.isOk());
- if (version == 1) {
+ if (getInterfaceVersion() <= 1) {
GTEST_SUCCEED() << "Device does not support the new API for overlay support";
return;
}
@@ -1225,17 +1229,21 @@
});
}
- sp<GraphicBuffer> allocate(::android::PixelFormat pixelFormat) {
+ sp<GraphicBuffer> allocate(uint32_t width, uint32_t height,
+ ::android::PixelFormat pixelFormat) {
return sp<GraphicBuffer>::make(
- static_cast<uint32_t>(getPrimaryDisplay().getDisplayWidth()),
- static_cast<uint32_t>(getPrimaryDisplay().getDisplayHeight()), pixelFormat,
- /*layerCount*/ 1U,
- (static_cast<uint64_t>(common::BufferUsage::CPU_WRITE_OFTEN) |
- static_cast<uint64_t>(common::BufferUsage::CPU_READ_OFTEN) |
- static_cast<uint64_t>(common::BufferUsage::COMPOSER_OVERLAY)),
+ width, height, pixelFormat, /*layerCount*/ 1U,
+ static_cast<uint64_t>(common::BufferUsage::CPU_WRITE_OFTEN) |
+ static_cast<uint64_t>(common::BufferUsage::CPU_READ_OFTEN) |
+ static_cast<uint64_t>(common::BufferUsage::COMPOSER_OVERLAY),
"VtsHalGraphicsComposer3_TargetTest");
}
+ sp<GraphicBuffer> allocate(::android::PixelFormat pixelFormat) {
+ return allocate(static_cast<uint32_t>(getPrimaryDisplay().getDisplayWidth()),
+ static_cast<uint32_t>(getPrimaryDisplay().getDisplayHeight()), pixelFormat);
+ }
+
void sendRefreshFrame(const VtsDisplay& display, const VsyncPeriodChangeTimeline* timeline) {
if (timeline != nullptr) {
// Refresh time should be before newVsyncAppliedTimeNanos
@@ -1749,6 +1757,104 @@
execute();
}
+TEST_P(GraphicsComposerAidlCommandTest, SetLayerBufferSlotsToClear) {
+ // Older HAL versions use a backwards compatible way of clearing buffer slots
+ const auto& [versionStatus, version] = mComposerClient->getInterfaceVersion();
+ ASSERT_TRUE(versionStatus.isOk());
+ if (version <= 1) {
+ GTEST_SUCCEED() << "HAL at version 1 or lower does not have "
+ "LayerCommand::bufferSlotsToClear.";
+ return;
+ }
+
+ const auto& [layerStatus, layer] =
+ mComposerClient->createLayer(getPrimaryDisplayId(), kBufferSlotCount);
+ EXPECT_TRUE(layerStatus.isOk());
+ auto& writer = getWriter(getPrimaryDisplayId());
+
+ // setup 3 buffers in the buffer cache, with the last buffer being active
+ // then emulate the Android platform code that clears all 3 buffer slots
+
+ const auto buffer1 = allocate(::android::PIXEL_FORMAT_RGBA_8888);
+ ASSERT_NE(nullptr, buffer1);
+ const auto handle1 = buffer1->handle;
+ writer.setLayerBuffer(getPrimaryDisplayId(), layer, /*slot*/ 0, handle1, /*acquireFence*/ -1);
+ execute();
+ ASSERT_TRUE(mReader.takeErrors().empty());
+
+ const auto buffer2 = allocate(::android::PIXEL_FORMAT_RGBA_8888);
+ ASSERT_NE(nullptr, buffer2);
+ const auto handle2 = buffer2->handle;
+ writer.setLayerBuffer(getPrimaryDisplayId(), layer, /*slot*/ 1, handle2, /*acquireFence*/ -1);
+ execute();
+ ASSERT_TRUE(mReader.takeErrors().empty());
+
+ const auto buffer3 = allocate(::android::PIXEL_FORMAT_RGBA_8888);
+ ASSERT_NE(nullptr, buffer3);
+ const auto handle3 = buffer3->handle;
+ writer.setLayerBuffer(getPrimaryDisplayId(), layer, /*slot*/ 2, handle3, /*acquireFence*/ -1);
+ execute();
+ ASSERT_TRUE(mReader.takeErrors().empty());
+
+ // Ensure we can clear all 3 buffer slots, even the active buffer - it is assumed the
+ // current active buffer's slot will be cleared, but still remain the active buffer and no
+ // errors will occur.
+ writer.setLayerBufferSlotsToClear(getPrimaryDisplayId(), layer, {0, 1, 2});
+ execute();
+ ASSERT_TRUE(mReader.takeErrors().empty());
+}
+
+TEST_P(GraphicsComposerAidlCommandTest, SetLayerBufferMultipleTimes) {
+ const auto& [layerStatus, layer] =
+ mComposerClient->createLayer(getPrimaryDisplayId(), kBufferSlotCount);
+ EXPECT_TRUE(layerStatus.isOk());
+ auto& writer = getWriter(getPrimaryDisplayId());
+
+ // Setup 3 buffers in the buffer cache, with the last buffer being active. Then, emulate the
+ // Android platform code that clears all 3 buffer slots by setting all but the active buffer
+ // slot to a placeholder buffer, and then restoring the active buffer.
+
+ // This is used on HALs that don't support setLayerBufferSlotsToClear (version <= 3.1).
+
+ const auto buffer1 = allocate(::android::PIXEL_FORMAT_RGBA_8888);
+ ASSERT_NE(nullptr, buffer1);
+ const auto handle1 = buffer1->handle;
+ writer.setLayerBuffer(getPrimaryDisplayId(), layer, /*slot*/ 0, handle1, /*acquireFence*/ -1);
+ execute();
+ ASSERT_TRUE(mReader.takeErrors().empty());
+
+ const auto buffer2 = allocate(::android::PIXEL_FORMAT_RGBA_8888);
+ ASSERT_NE(nullptr, buffer2);
+ const auto handle2 = buffer2->handle;
+ writer.setLayerBuffer(getPrimaryDisplayId(), layer, /*slot*/ 1, handle2, /*acquireFence*/ -1);
+ execute();
+ ASSERT_TRUE(mReader.takeErrors().empty());
+
+ const auto buffer3 = allocate(::android::PIXEL_FORMAT_RGBA_8888);
+ ASSERT_NE(nullptr, buffer3);
+ const auto handle3 = buffer3->handle;
+ writer.setLayerBuffer(getPrimaryDisplayId(), layer, /*slot*/ 2, handle3, /*acquireFence*/ -1);
+ execute();
+ ASSERT_TRUE(mReader.takeErrors().empty());
+
+ // Older versions of the HAL clear all but the active buffer slot with a placeholder buffer,
+ // and then restoring the current active buffer at the end
+ auto clearSlotBuffer = allocate(1u, 1u, ::android::PIXEL_FORMAT_RGB_888);
+ ASSERT_NE(nullptr, clearSlotBuffer);
+ auto clearSlotBufferHandle = clearSlotBuffer->handle;
+
+ // clear buffer slots 0 and 1 with new layer commands... and then...
+ writer.setLayerBufferWithNewCommand(getPrimaryDisplayId(), layer, /* slot */ 0,
+ clearSlotBufferHandle, /*acquireFence*/ -1);
+ writer.setLayerBufferWithNewCommand(getPrimaryDisplayId(), layer, /* slot */ 1,
+ clearSlotBufferHandle, /*acquireFence*/ -1);
+ // ...reset the layer buffer to the current active buffer slot with a final new command
+ writer.setLayerBufferWithNewCommand(getPrimaryDisplayId(), layer, /*slot*/ 2, nullptr,
+ /*acquireFence*/ -1);
+ execute();
+ ASSERT_TRUE(mReader.takeErrors().empty());
+}
+
TEST_P(GraphicsComposerAidlCommandTest, SetLayerSurfaceDamage) {
const auto& [layerStatus, layer] =
mComposerClient->createLayer(getPrimaryDisplayId(), kBufferSlotCount);
@@ -2290,6 +2396,11 @@
}
TEST_P(GraphicsComposerAidlCommandTest, SetRefreshRateChangedCallbackDebug_Unsupported) {
+ if (getInterfaceVersion() <= 1) {
+ GTEST_SUCCEED() << "Capability::REFRESH_RATE_CHANGED_CALLBACK_DEBUG is "
+ "not supported on older version of the service";
+ return;
+ }
if (!hasCapability(Capability::REFRESH_RATE_CHANGED_CALLBACK_DEBUG)) {
auto status = mComposerClient->setRefreshRateChangedCallbackDebugEnabled(
getPrimaryDisplayId(), /*enabled*/ true);
@@ -2306,6 +2417,11 @@
}
TEST_P(GraphicsComposerAidlCommandTest, SetRefreshRateChangedCallbackDebug_Enabled) {
+ if (getInterfaceVersion() <= 1) {
+ GTEST_SUCCEED() << "Capability::REFRESH_RATE_CHANGED_CALLBACK_DEBUG is "
+ "not supported on older version of the service";
+ return;
+ }
if (!hasCapability(Capability::REFRESH_RATE_CHANGED_CALLBACK_DEBUG)) {
GTEST_SUCCEED() << "Capability::REFRESH_RATE_CHANGED_CALLBACK_DEBUG is not supported";
return;
@@ -2335,6 +2451,11 @@
TEST_P(GraphicsComposerAidlCommandTest,
SetRefreshRateChangedCallbackDebugEnabled_noCallbackWhenIdle) {
+ if (getInterfaceVersion() <= 1) {
+ GTEST_SUCCEED() << "Capability::REFRESH_RATE_CHANGED_CALLBACK_DEBUG is "
+ "not supported on older version of the service";
+ return;
+ }
if (!hasCapability(Capability::REFRESH_RATE_CHANGED_CALLBACK_DEBUG)) {
GTEST_SUCCEED() << "Capability::REFRESH_RATE_CHANGED_CALLBACK_DEBUG is not supported";
return;
@@ -2392,6 +2513,11 @@
TEST_P(GraphicsComposerAidlCommandTest,
SetRefreshRateChangedCallbackDebugEnabled_SetActiveConfigWithConstraints) {
+ if (getInterfaceVersion() <= 1) {
+ GTEST_SUCCEED() << "Capability::REFRESH_RATE_CHANGED_CALLBACK_DEBUG is "
+ "not supported on older version of the service";
+ return;
+ }
if (!hasCapability(Capability::REFRESH_RATE_CHANGED_CALLBACK_DEBUG)) {
GTEST_SUCCEED() << "Capability::REFRESH_RATE_CHANGED_CALLBACK_DEBUG is not supported";
return;
@@ -2494,29 +2620,13 @@
}
}
-TEST_P(GraphicsComposerAidlCommandTest, MultiThreadedPresent) {
- std::vector<VtsDisplay*> displays;
- for (auto& display : mDisplays) {
- if (hasDisplayCapability(display.getDisplayId(),
- DisplayCapability::MULTI_THREADED_PRESENT)) {
- displays.push_back(&display);
- }
- }
- if (displays.size() <= 1u) {
- return;
- }
- // TODO(b/251842321): Try to present on multiple threads.
-}
-
/**
* Test Capability::SKIP_VALIDATE
*
* Capability::SKIP_VALIDATE has been deprecated and should not be enabled.
*/
TEST_P(GraphicsComposerAidlCommandTest, SkipValidateDeprecatedTest) {
- const auto& [versionStatus, version] = mComposerClient->getInterfaceVersion();
- ASSERT_TRUE(versionStatus.isOk());
- if (version <= 1) {
+ if (getInterfaceVersion() <= 1) {
GTEST_SUCCEED() << "HAL at version 1 or lower can contain Capability::SKIP_VALIDATE.";
return;
}
@@ -2583,4 +2693,4 @@
}
return RUN_ALL_TESTS();
-}
\ No newline at end of file
+}
diff --git a/health/aidl/vts/functional/VtsHalHealthTargetTest.cpp b/health/aidl/vts/functional/VtsHalHealthTargetTest.cpp
index 69d4789..783ce11 100644
--- a/health/aidl/vts/functional/VtsHalHealthTargetTest.cpp
+++ b/health/aidl/vts/functional/VtsHalHealthTargetTest.cpp
@@ -257,7 +257,7 @@
BatteryChargingPolicy value;
/* set ChargingPolicy*/
- status = health->setChargingPolicy(static_cast<BatteryChargingPolicy>(2)); // LONG_LIFE
+ status = health->setChargingPolicy(BatteryChargingPolicy::LONG_LIFE);
ASSERT_THAT(status, AnyOf(IsOk(), ExceptionIs(EX_UNSUPPORTED_OPERATION)));
if (!status.isOk()) return;
@@ -265,7 +265,9 @@
status = health->getChargingPolicy(&value);
ASSERT_THAT(status, AnyOf(IsOk(), ExceptionIs(EX_UNSUPPORTED_OPERATION)));
if (!status.isOk()) return;
- ASSERT_THAT(static_cast<int>(value), Eq(2));
+ // the result of getChargingPolicy will be one of default(1), ADAPTIVE_AON(2)
+ // ADAPTIVE_AC(3) or LONG_LIFE(4). default(1) means NOT_SUPPORT
+ ASSERT_THAT(static_cast<int>(value), AnyOf(Eq(1), Eq(4)));
}
MATCHER(IsValidHealthData, "") {
diff --git a/identity/OWNERS b/identity/OWNERS
index 6969910..9353bbc 100644
--- a/identity/OWNERS
+++ b/identity/OWNERS
@@ -1,2 +1,4 @@
+# Bug component: 1084909
+
swillden@google.com
zeuthen@google.com
diff --git a/identity/aidl/vts/Android.bp b/identity/aidl/vts/Android.bp
index 5e303bb..6f7ab54 100644
--- a/identity/aidl/vts/Android.bp
+++ b/identity/aidl/vts/Android.bp
@@ -61,16 +61,3 @@
],
require_root: true,
}
-
-java_test_host {
- name: "IdentityCredentialImplementedTest",
- libs: [
- "tradefed",
- "vts-core-tradefed-harness",
- ],
- srcs: ["src/**/*.java"],
- test_suites: [
- "vts",
- ],
- test_config: "IdentityCredentialImplementedTest.xml",
-}
diff --git a/identity/aidl/vts/IdentityCredentialImplementedTest.xml b/identity/aidl/vts/IdentityCredentialImplementedTest.xml
deleted file mode 100644
index 4276ae6..0000000
--- a/identity/aidl/vts/IdentityCredentialImplementedTest.xml
+++ /dev/null
@@ -1,22 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2022 The Android Open Source Project
-
-Licensed under the Apache License, Version 2.0 (the "License");
-you may not use this file except in compliance with the License.
-You may obtain a copy of the License at
-
-http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-See the License for the specific language governing permissions and
-limitations under the License.
--->
-<configuration description="Runs IdentityCredentialImplementedTest">
- <target_preparer class="com.android.tradefed.targetprep.RootTargetPreparer" />
-
- <test class="com.android.tradefed.testtype.HostTest" >
- <option name="jar" value="IdentityCredentialImplementedTest.jar" />
- </test>
-</configuration>
diff --git a/identity/aidl/vts/src/com/android/tests/security/identity/IdentityCredentialImplementedTest.java b/identity/aidl/vts/src/com/android/tests/security/identity/IdentityCredentialImplementedTest.java
deleted file mode 100644
index 4936f8c..0000000
--- a/identity/aidl/vts/src/com/android/tests/security/identity/IdentityCredentialImplementedTest.java
+++ /dev/null
@@ -1,68 +0,0 @@
-/*
- * Copyright (C) 2022 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.tests.security.identity;
-
-import static org.junit.Assert.fail;
-import static org.junit.Assume.assumeTrue;
-
-import android.platform.test.annotations.RequiresDevice;
-import com.android.tradefed.device.DeviceNotAvailableException;
-import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
-import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-// This is a host-test which executes shell commands on the device. It would be
-// nicer to have this be a Device test (like CTS) but this is currently not
-// possible, see https://source.android.com/docs/core/tests/vts
-
-@RunWith(DeviceJUnit4ClassRunner.class)
-public class IdentityCredentialImplementedTest extends BaseHostJUnit4Test {
- // Returns the ro.vendor.api_level or 0 if not set.
- //
- // Throws NumberFormatException if ill-formatted.
- //
- // Throws DeviceNotAvailableException if device is not available.
- //
- private int getVendorApiLevel() throws NumberFormatException, DeviceNotAvailableException {
- String vendorApiLevelString =
- getDevice().executeShellCommand("getprop ro.vendor.api_level").trim();
- if (vendorApiLevelString.isEmpty()) {
- return 0;
- }
- return Integer.parseInt(vendorApiLevelString);
- }
-
- // As of Android 14 VSR (vendor API level 34), Identity Credential is required at feature
- // version 202301 or later.
- @RequiresDevice
- @Test
- public void testIdentityCredentialIsImplemented() throws Exception {
- int vendorApiLevel = getVendorApiLevel();
- assumeTrue(vendorApiLevel >= 34);
-
- final String minimumFeatureVersionNeeded = "202301";
-
- String result = getDevice().executeShellCommand(
- "pm has-feature android.hardware.identity_credential "
- + minimumFeatureVersionNeeded);
- if (!result.trim().equals("true")) {
- fail("Identity Credential feature version " + minimumFeatureVersionNeeded
- + " required but not found");
- }
- }
-}
diff --git a/neuralnetworks/aidl/vts/functional/GeneratedTestHarness.cpp b/neuralnetworks/aidl/vts/functional/GeneratedTestHarness.cpp
index dcf8451..e344458 100644
--- a/neuralnetworks/aidl/vts/functional/GeneratedTestHarness.cpp
+++ b/neuralnetworks/aidl/vts/functional/GeneratedTestHarness.cpp
@@ -736,8 +736,8 @@
// If a sync fence is returned, try start another run waiting for the sync
// fence.
if (testConfig.reusable) {
- ret = execution->executeFenced(waitFor, kNoDeadline, kNoDuration,
- &executionResult);
+ // Nothing to do because at most one execution may occur on a reusable
+ // execution object at any given time.
} else if (testConfig.useConfig) {
ret = preparedModel->executeFencedWithConfig(
request, waitFor,
diff --git a/radio/1.5/vts/functional/radio_hidl_hal_api.cpp b/radio/1.5/vts/functional/radio_hidl_hal_api.cpp
index 5539b9c..316c308 100644
--- a/radio/1.5/vts/functional/radio_hidl_hal_api.cpp
+++ b/radio/1.5/vts/functional/radio_hidl_hal_api.cpp
@@ -565,9 +565,9 @@
serial = GetRandomSerialNumber();
::android::hardware::radio::V1_5::RadioAccessSpecifier::Bands band17;
- band17.eutranBands() = {::android::hardware::radio::V1_5::EutranBands::BAND_17};
+ band17.eutranBands({::android::hardware::radio::V1_5::EutranBands::BAND_17});
::android::hardware::radio::V1_5::RadioAccessSpecifier::Bands band20;
- band20.eutranBands() = {::android::hardware::radio::V1_5::EutranBands::BAND_20};
+ band20.eutranBands({::android::hardware::radio::V1_5::EutranBands::BAND_20});
::android::hardware::radio::V1_5::RadioAccessSpecifier specifier17 = {
.radioAccessNetwork = ::android::hardware::radio::V1_5::RadioAccessNetworks::EUTRAN,
.bands = band17,
diff --git a/secure_element/aidl/android/hardware/secure_element/ISecureElement.aidl b/secure_element/aidl/android/hardware/secure_element/ISecureElement.aidl
index 8c0dd6d..800494a 100644
--- a/secure_element/aidl/android/hardware/secure_element/ISecureElement.aidl
+++ b/secure_element/aidl/android/hardware/secure_element/ISecureElement.aidl
@@ -117,6 +117,9 @@
* closed by this operation.
* HAL service must send onStateChange() with connected equal to true
* after resetting and all the re-initialization has been successfully completed.
+ *
+ * @throws ServiceSpecificException on error with the following code:
+ * - FAILED if the service was unable to reset the secure element.
*/
void reset();
diff --git a/secure_element/aidl/vts/VtsHalSecureElementTargetTest.cpp b/secure_element/aidl/vts/VtsHalSecureElementTargetTest.cpp
index 0925a21..97b4e27 100644
--- a/secure_element/aidl/vts/VtsHalSecureElementTargetTest.cpp
+++ b/secure_element/aidl/vts/VtsHalSecureElementTargetTest.cpp
@@ -83,10 +83,15 @@
void expectCallbackHistory(std::vector<bool>&& want) {
std::unique_lock<std::mutex> l(m);
- cv.wait_for(l, 2s, [&]() { return history.size() >= want.size(); });
+ cv.wait_for(l, 5s, [&]() { return history.size() >= want.size(); });
EXPECT_THAT(history, ElementsAreArray(want));
}
+ void resetCallbackHistory() {
+ std::unique_lock<std::mutex> l(m);
+ history.clear();
+ }
+
private:
std::mutex m; // guards history
std::condition_variable cv;
@@ -118,7 +123,9 @@
}
void TearDown() override {
+ secure_element_callback_->resetCallbackHistory();
EXPECT_OK(secure_element_->reset());
+ secure_element_callback_->expectCallbackHistory({false, true});
secure_element_ = nullptr;
secure_element_callback_ = nullptr;
}
@@ -284,14 +291,21 @@
TEST_P(SecureElementAidl, transmit) {
std::vector<uint8_t> response;
+ LogicalChannelResponse logical_channel_response;
- // transmit called after init shall succeed.
- // Note: no channel is opened for this test and the transmit
- // response will have the status SW_LOGICAL_CHANNEL_NOT_SUPPORTED.
- // The transmit response shall be larger than 2 bytes as it includes the
- // status code.
- EXPECT_OK(secure_element_->transmit(kDataApdu, &response));
- EXPECT_GE(response.size(), 2u);
+ // Note: no channel is opened for this test
+ // transmit() will return an empty response with the error
+ // code CHANNEL_NOT_AVAILABLE when the SE cannot be
+ // communicated with.
+ EXPECT_ERR(secure_element_->transmit(kDataApdu, &response));
+
+ EXPECT_OK(secure_element_->openLogicalChannel(kSelectableAid, 0x00, &logical_channel_response));
+ EXPECT_GE(logical_channel_response.selectResponse.size(), 2u);
+ EXPECT_GE(logical_channel_response.channelNumber, 1u);
+ EXPECT_LE(logical_channel_response.channelNumber, 19u);
+
+ // transmit called on the logical channel should succeed.
+ EXPECT_EQ(transmit(logical_channel_response.channelNumber), 0x9000);
}
GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(SecureElementAidl);
diff --git a/security/keymint/aidl/vts/functional/KeyMintTest.cpp b/security/keymint/aidl/vts/functional/KeyMintTest.cpp
index c6b8906..63b2e73 100644
--- a/security/keymint/aidl/vts/functional/KeyMintTest.cpp
+++ b/security/keymint/aidl/vts/functional/KeyMintTest.cpp
@@ -26,6 +26,7 @@
#include <openssl/ec.h>
#include <openssl/evp.h>
#include <openssl/mem.h>
+#include <openssl/x509.h>
#include <openssl/x509v3.h>
#include <cutils/properties.h>
@@ -590,8 +591,7 @@
return name.substr(pos + 1);
}
-bool matching_rp_instance(const string& km_name,
- std::shared_ptr<IRemotelyProvisionedComponent>* rp) {
+std::shared_ptr<IRemotelyProvisionedComponent> matching_rp_instance(const std::string& km_name) {
string km_suffix = device_suffix(km_name);
vector<string> rp_names =
@@ -601,11 +601,10 @@
// KeyMint instance, assume they match.
if (device_suffix(rp_name) == km_suffix && AServiceManager_isDeclared(rp_name.c_str())) {
::ndk::SpAIBinder binder(AServiceManager_waitForService(rp_name.c_str()));
- *rp = IRemotelyProvisionedComponent::fromBinder(binder);
- return true;
+ return IRemotelyProvisionedComponent::fromBinder(binder);
}
}
- return false;
+ return nullptr;
}
} // namespace
@@ -1061,6 +1060,42 @@
}
/*
+ * NewKeyGenerationTest.RsaWithSpecifiedValidity
+ *
+ * Verifies that KeyMint respects specified NOT_BEFORE and NOT_AFTER certificate dates.
+ */
+TEST_P(NewKeyGenerationTest, RsaWithSpecifiedValidity) {
+ vector<uint8_t> key_blob;
+ vector<KeyCharacteristics> key_characteristics;
+ ASSERT_EQ(ErrorCode::OK,
+ GenerateKey(AuthorizationSetBuilder()
+ .RsaSigningKey(2048, 65537)
+ .Digest(Digest::NONE)
+ .Padding(PaddingMode::NONE)
+ .Authorization(TAG_CERTIFICATE_NOT_BEFORE,
+ 1183806000000 /* 2007-07-07T11:00:00Z */)
+ .Authorization(TAG_CERTIFICATE_NOT_AFTER,
+ 1916049600000 /* 2030-09-19T12:00:00Z */),
+ &key_blob, &key_characteristics));
+ ASSERT_GT(cert_chain_.size(), 0);
+
+ X509_Ptr cert(parse_cert_blob(cert_chain_[0].encodedCertificate));
+ ASSERT_TRUE(!!cert.get());
+
+ const ASN1_TIME* not_before = X509_get0_notBefore(cert.get());
+ ASSERT_NE(not_before, nullptr);
+ time_t not_before_time;
+ ASSERT_EQ(ASN1_TIME_to_time_t(not_before, ¬_before_time), 1);
+ EXPECT_EQ(not_before_time, 1183806000);
+
+ const ASN1_TIME* not_after = X509_get0_notAfter(cert.get());
+ ASSERT_NE(not_after, nullptr);
+ time_t not_after_time;
+ ASSERT_EQ(ASN1_TIME_to_time_t(not_after, ¬_after_time), 1);
+ EXPECT_EQ(not_after_time, 1916049600);
+}
+
+/*
* NewKeyGenerationTest.RsaWithAttestation
*
* Verifies that keymint can generate all required RSA key sizes with attestation, and that the
@@ -1140,11 +1175,14 @@
GTEST_SKIP() << "RKP support is not required on this platform";
}
- // There should be an IRemotelyProvisionedComponent instance associated with the KeyMint
- // instance.
- std::shared_ptr<IRemotelyProvisionedComponent> rp;
- ASSERT_TRUE(matching_rp_instance(GetParam(), &rp))
- << "No IRemotelyProvisionedComponent found that matches KeyMint device " << GetParam();
+ // Check for an IRemotelyProvisionedComponent instance associated with the
+ // KeyMint instance.
+ std::shared_ptr<IRemotelyProvisionedComponent> rp = matching_rp_instance(GetParam());
+ if (rp == nullptr && SecLevel() == SecurityLevel::STRONGBOX) {
+ GTEST_SKIP() << "Encountered StrongBox implementation that does not support RKP";
+ }
+ ASSERT_NE(rp, nullptr) << "No IRemotelyProvisionedComponent found that matches KeyMint device "
+ << GetParam();
// Generate a P-256 keypair to use as an attestation key.
MacedPublicKey macedPubKey;
@@ -1218,11 +1256,14 @@
GTEST_SKIP() << "RKP support is not required on this platform";
}
- // There should be an IRemotelyProvisionedComponent instance associated with the KeyMint
- // instance.
- std::shared_ptr<IRemotelyProvisionedComponent> rp;
- ASSERT_TRUE(matching_rp_instance(GetParam(), &rp))
- << "No IRemotelyProvisionedComponent found that matches KeyMint device " << GetParam();
+ // Check for an IRemotelyProvisionedComponent instance associated with the
+ // KeyMint instance.
+ std::shared_ptr<IRemotelyProvisionedComponent> rp = matching_rp_instance(GetParam());
+ if (rp == nullptr && SecLevel() == SecurityLevel::STRONGBOX) {
+ GTEST_SKIP() << "Encountered StrongBox implementation that does not support RKP";
+ }
+ ASSERT_NE(rp, nullptr) << "No IRemotelyProvisionedComponent found that matches KeyMint device "
+ << GetParam();
// Generate a P-256 keypair to use as an attestation key.
MacedPublicKey macedPubKey;
diff --git a/security/keymint/support/remote_prov_utils.cpp b/security/keymint/support/remote_prov_utils.cpp
index 086ee79..ffcaa95 100644
--- a/security/keymint/support/remote_prov_utils.cpp
+++ b/security/keymint/support/remote_prov_utils.cpp
@@ -22,6 +22,7 @@
#include "aidl/android/hardware/security/keymint/IRemotelyProvisionedComponent.h"
#include <aidl/android/hardware/security/keymint/RpcHardwareInfo.h>
+#include <android-base/macros.h>
#include <android-base/properties.h>
#include <cppbor.h>
#include <hwtrust/hwtrust.h>
@@ -43,6 +44,7 @@
constexpr int32_t kBccPayloadSubjPubKey = -4670552;
constexpr int32_t kBccPayloadKeyUsage = -4670553;
constexpr int kP256AffinePointSize = 32;
+constexpr uint32_t kNumTeeDeviceInfoEntries = 14;
using EC_KEY_Ptr = bssl::UniquePtr<EC_KEY>;
using EVP_PKEY_Ptr = bssl::UniquePtr<EVP_PKEY>;
@@ -388,6 +390,11 @@
return entryName + " has an invalid value.\n";
}
+bool isTeeDeviceInfo(const cppbor::Map& devInfo) {
+ return devInfo.get("security_level") && devInfo.get("security_level")->asTstr() &&
+ devInfo.get("security_level")->asTstr()->value() == "tee";
+}
+
ErrMsgOr<std::unique_ptr<cppbor::Map>> parseAndValidateDeviceInfo(
const std::vector<uint8_t>& deviceInfoBytes, IRemotelyProvisionedComponent* provisionable,
bool isFactory) {
@@ -396,6 +403,21 @@
const cppbor::Array kValidSecurityLevels = {"tee", "strongbox"};
const cppbor::Array kValidAttIdStates = {"locked", "open"};
const cppbor::Array kValidFused = {0, 1};
+ constexpr std::array<std::string_view, kNumTeeDeviceInfoEntries> kDeviceInfoKeys = {
+ "brand",
+ "manufacturer",
+ "product",
+ "model",
+ "device",
+ "vb_state",
+ "bootloader_state",
+ "vbmeta_digest",
+ "os_version",
+ "system_patch_level",
+ "boot_patch_level",
+ "vendor_patch_level",
+ "security_level",
+ "fused"};
struct AttestationIdEntry {
const char* id;
@@ -439,20 +461,48 @@
}
std::string error;
+ std::string tmp;
+ std::set<std::string_view> previousKeys;
switch (info.versionNumber) {
case 3:
+ if (isTeeDeviceInfo(*parsed) && parsed->size() != kNumTeeDeviceInfoEntries) {
+ error += fmt::format(
+ "Err: Incorrect number of device info entries. Expected {} but got "
+ "{}\n",
+ kNumTeeDeviceInfoEntries, parsed->size());
+ }
+ // TEE IRPC instances require all entries to be present in DeviceInfo. Non-TEE instances
+ // may omit `os_version`
+ if (!isTeeDeviceInfo(*parsed) && (parsed->size() != kNumTeeDeviceInfoEntries &&
+ parsed->size() != kNumTeeDeviceInfoEntries - 1)) {
+ error += fmt::format(
+ "Err: Incorrect number of device info entries. Expected {} or {} but got "
+ "{}\n",
+ kNumTeeDeviceInfoEntries - 1, kNumTeeDeviceInfoEntries, parsed->size());
+ }
+ for (auto& [key, _] : *parsed) {
+ const std::string& keyValue = key->asTstr()->value();
+ if (!previousKeys.insert(keyValue).second) {
+ error += "Err: Duplicate device info entry: <" + keyValue + ">,\n";
+ }
+ if (std::find(kDeviceInfoKeys.begin(), kDeviceInfoKeys.end(), keyValue) ==
+ kDeviceInfoKeys.end()) {
+ error += "Err: Unrecognized key entry: <" + key->asTstr()->value() + ">,\n";
+ }
+ }
+ FALLTHROUGH_INTENDED;
case 2:
for (const auto& entry : kAttestationIdEntrySet) {
- error += checkMapEntry(isFactory && !entry.alwaysValidate, *parsed, cppbor::TSTR,
- entry.id);
+ tmp = checkMapEntry(isFactory && !entry.alwaysValidate, *parsed, cppbor::TSTR,
+ entry.id);
}
- if (!error.empty()) {
- return error +
- "Attestation IDs are missing or malprovisioned. If this test is being\n"
- "run against an early proto or EVT build, this error is probably WAI\n"
- "and indicates that Device IDs were not provisioned in the factory. If\n"
- "this error is returned on a DVT or later build revision, then\n"
- "something is likely wrong with the factory provisioning process.";
+ if (!tmp.empty()) {
+ error += tmp +
+ "Attestation IDs are missing or malprovisioned. If this test is being\n"
+ "run against an early proto or EVT build, this error is probably WAI\n"
+ "and indicates that Device IDs were not provisioned in the factory. If\n"
+ "this error is returned on a DVT or later build revision, then\n"
+ "something is likely wrong with the factory provisioning process.";
}
// TODO: Refactor the KeyMint code that validates these fields and include it here.
error += checkMapEntry(isFactory, *parsed, cppbor::TSTR, "vb_state", kValidVbStates);
@@ -465,8 +515,7 @@
error += checkMapEntry(isFactory, *parsed, cppbor::UINT, "fused", kValidFused);
error += checkMapEntry(isFactory, *parsed, cppbor::TSTR, "security_level",
kValidSecurityLevels);
- if (parsed->get("security_level") && parsed->get("security_level")->asTstr() &&
- parsed->get("security_level")->asTstr()->value() == "tee") {
+ if (isTeeDeviceInfo(*parsed)) {
error += checkMapEntry(isFactory, *parsed, cppbor::TSTR, "os_version");
}
break;
@@ -570,7 +619,7 @@
}
// BCC is [ pubkey, + BccEntry]
- auto bccContents = validateBcc(bcc->asArray(), hwtrust::DiceChain::Kind::kProtectedData);
+ auto bccContents = validateBcc(bcc->asArray(), hwtrust::DiceChain::Kind::kVsr13);
if (!bccContents) {
return bccContents.message() + "\n" + prettyPrint(bcc.get());
}
@@ -861,7 +910,7 @@
}
// DICE chain is [ pubkey, + DiceChainEntry ].
- auto diceContents = validateBcc(diceCertChain, hwtrust::DiceChain::Kind::kAuthenticatedMessage);
+ auto diceContents = validateBcc(diceCertChain, hwtrust::DiceChain::Kind::kVsr14);
if (!diceContents) {
return diceContents.message() + "\n" + prettyPrint(diceCertChain);
}
diff --git a/security/rkp/CHANGELOG.md b/security/rkp/CHANGELOG.md
index 9409a6d..f425284 100644
--- a/security/rkp/CHANGELOG.md
+++ b/security/rkp/CHANGELOG.md
@@ -31,7 +31,7 @@
* IRemotelyProvisionedComponent
* The need for an EEK has been removed. There is no longer an encrypted portion of the CSR.
* Keys for new CSR format must be generated with test mode set to false, effectively removing test
- mode in the new CSR flow. Old behavior is kept unchanged for backwards compatibility.
+ mode in the new CSR flow.
* The schema for the CSR itself has been significantly simplified, please see
IRemotelyProvisionedComponent.aidl for more details. Notably,
* the chain of signing, MACing, and encryption operations has been replaced with a single
diff --git a/security/rkp/aidl/android/hardware/security/keymint/IRemotelyProvisionedComponent.aidl b/security/rkp/aidl/android/hardware/security/keymint/IRemotelyProvisionedComponent.aidl
index 35b83dd..7960c7f 100644
--- a/security/rkp/aidl/android/hardware/security/keymint/IRemotelyProvisionedComponent.aidl
+++ b/security/rkp/aidl/android/hardware/security/keymint/IRemotelyProvisionedComponent.aidl
@@ -144,9 +144,9 @@
byte[] generateEcdsaP256KeyPair(in boolean testMode, out MacedPublicKey macedPublicKey);
/**
- * This method can be removed in version 3 of the HAL. The header is kept around for
- * backwards compatibility purposes. From v3, this method is allowed to raise a
- * ServiceSpecificException with an error code of STATUS_REMOVED.
+ * This method has been deprecated since version 3 of the HAL. The header is kept around for
+ * backwards compatibility purposes. From v3, this method must raise a ServiceSpecificException
+ * with an error code of STATUS_REMOVED.
*
* For v1 and v2 implementations:
* generateCertificateRequest creates a certificate request to be sent to the provisioning
diff --git a/security/rkp/aidl/vts/functional/VtsRemotelyProvisionedComponentTests.cpp b/security/rkp/aidl/vts/functional/VtsRemotelyProvisionedComponentTests.cpp
index bf40976..9f68bfa 100644
--- a/security/rkp/aidl/vts/functional/VtsRemotelyProvisionedComponentTests.cpp
+++ b/security/rkp/aidl/vts/functional/VtsRemotelyProvisionedComponentTests.cpp
@@ -408,16 +408,8 @@
ASSERT_FALSE(HasFatalFailure());
if (rpcHardwareInfo.versionNumber >= VERSION_WITHOUT_TEST_MODE) {
- bytevec keysToSignMac;
- DeviceInfo deviceInfo;
- ProtectedData protectedData;
- auto status = provisionable_->generateCertificateRequest(
- false, {}, {}, {}, &deviceInfo, &protectedData, &keysToSignMac);
- if (!status.isOk() && (status.getServiceSpecificError() ==
- BnRemotelyProvisionedComponent::STATUS_REMOVED)) {
- GTEST_SKIP() << "This test case applies to RKP v3+ only if "
- << "generateCertificateRequest() is implemented.";
- }
+ GTEST_SKIP() << "This test case only applies to RKP v1 and v2. "
+ << "RKP version discovered: " << rpcHardwareInfo.versionNumber;
}
}
};
@@ -798,6 +790,20 @@
BnRemotelyProvisionedComponent::STATUS_TEST_KEY_IN_PRODUCTION_REQUEST);
}
+/**
+ * Call generateCertificateRequest(). Make sure it's removed.
+ */
+TEST_P(CertificateRequestV2Test, CertificateRequestV1Removed) {
+ bytevec keysToSignMac;
+ DeviceInfo deviceInfo;
+ ProtectedData protectedData;
+ auto status = provisionable_->generateCertificateRequest(
+ true /* testMode */, {} /* keysToSign */, {} /* EEK chain */, challenge_, &deviceInfo,
+ &protectedData, &keysToSignMac);
+ ASSERT_FALSE(status.isOk()) << status.getMessage();
+ EXPECT_EQ(status.getServiceSpecificError(), BnRemotelyProvisionedComponent::STATUS_REMOVED);
+}
+
void parse_root_of_trust(const vector<uint8_t>& attestation_cert,
vector<uint8_t>* verified_boot_key, VerifiedBoot* verified_boot_state,
bool* device_locked, vector<uint8_t>* verified_boot_hash) {
diff --git a/tests/extension/vibrator/aidl/Android.bp b/tests/extension/vibrator/aidl/Android.bp
index 20df5bb..0306dca 100644
--- a/tests/extension/vibrator/aidl/Android.bp
+++ b/tests/extension/vibrator/aidl/Android.bp
@@ -38,5 +38,11 @@
enabled: false,
},
},
- versions: ["1"],
+ frozen: true,
+ versions_with_info: [
+ {
+ version: "1",
+ imports: ["android.hardware.vibrator-V2"],
+ },
+ ],
}
diff --git a/tests/extension/vibrator/aidl/default/Android.bp b/tests/extension/vibrator/aidl/default/Android.bp
index 0f3895f..5e156af 100644
--- a/tests/extension/vibrator/aidl/default/Android.bp
+++ b/tests/extension/vibrator/aidl/default/Android.bp
@@ -29,6 +29,6 @@
"libbase",
"libbinder_ndk",
"android.hardware.vibrator-V2-ndk",
- "android.hardware.tests.extension.vibrator-V2-ndk",
+ "android.hardware.tests.extension.vibrator-V1-ndk",
],
}
diff --git a/uwb/aidl/aidl_api/android.hardware.uwb.fira_android/current/android/hardware/uwb/fira_android/UwbVendorSessionAppConfigTlvTypes.aidl b/uwb/aidl/aidl_api/android.hardware.uwb.fira_android/current/android/hardware/uwb/fira_android/UwbVendorSessionAppConfigTlvTypes.aidl
index 8413f06..c3ac401 100644
--- a/uwb/aidl/aidl_api/android.hardware.uwb.fira_android/current/android/hardware/uwb/fira_android/UwbVendorSessionAppConfigTlvTypes.aidl
+++ b/uwb/aidl/aidl_api/android.hardware.uwb.fira_android/current/android/hardware/uwb/fira_android/UwbVendorSessionAppConfigTlvTypes.aidl
@@ -40,6 +40,7 @@
CCC_UWB_CONFIG_ID = 164,
CCC_PULSESHAPE_COMBO = 165,
CCC_URSK_TTL = 166,
+ CCC_LAST_INDEX_USED = 168,
NB_OF_RANGE_MEASUREMENTS = 227,
NB_OF_AZIMUTH_MEASUREMENTS = 228,
NB_OF_ELEVATION_MEASUREMENTS = 229,
diff --git a/uwb/aidl/android/hardware/uwb/fira_android/UwbVendorSessionAppConfigTlvTypes.aidl b/uwb/aidl/android/hardware/uwb/fira_android/UwbVendorSessionAppConfigTlvTypes.aidl
index f303ed9..65bb1c9 100644
--- a/uwb/aidl/android/hardware/uwb/fira_android/UwbVendorSessionAppConfigTlvTypes.aidl
+++ b/uwb/aidl/android/hardware/uwb/fira_android/UwbVendorSessionAppConfigTlvTypes.aidl
@@ -46,6 +46,8 @@
CCC_PULSESHAPE_COMBO = 0xA5,
/** 2 byte data */
CCC_URSK_TTL = 0xA6,
+ /** 4 byte data */
+ CCC_LAST_INDEX_USED = 0xA8,
/**
* Range 0xE3 - 0xFF is reserved for proprietary use in the FIRA spec.
diff --git a/wifi/1.6/default/Android.bp b/wifi/1.6/default/Android.bp
index 0f98e71..6038e36 100644
--- a/wifi/1.6/default/Android.bp
+++ b/wifi/1.6/default/Android.bp
@@ -26,6 +26,7 @@
"hidl_feature_disable_ap", // WIFI_HIDL_FEATURE_DISABLE_AP
"hidl_feature_disable_ap_mac_randomization", // WIFI_HIDL_FEATURE_DISABLE_AP_MAC_RANDOMIZATION
"avoid_iface_reset_mac_change", // WIFI_AVOID_IFACE_RESET_MAC_CHANGE
+ "wifi_skip_state_toggle_off_on_for_nan", // WIFI_SKIP_STATE_TOGGLE_OFF_ON_FOR_NAN
],
value_variables: [
"hal_interface_combinations", // WIFI_HAL_INTERFACE_COMBINATIONS
@@ -53,6 +54,9 @@
avoid_iface_reset_mac_change: {
cppflags: ["-DWIFI_AVOID_IFACE_RESET_MAC_CHANGE"],
},
+ wifi_skip_state_toggle_off_on_for_nan: {
+ cppflags: ["-DWIFI_SKIP_STATE_TOGGLE_OFF_ON_FOR_NAN"],
+ },
hal_interface_combinations: {
cppflags: ["-DWIFI_HAL_INTERFACE_COMBINATIONS=%s"],
},
diff --git a/wifi/1.6/default/wifi_nan_iface.cpp b/wifi/1.6/default/wifi_nan_iface.cpp
index ac2ebc9..4c61ba7 100644
--- a/wifi/1.6/default/wifi_nan_iface.cpp
+++ b/wifi/1.6/default/wifi_nan_iface.cpp
@@ -453,6 +453,7 @@
// Register for iface state toggle events.
iface_util::IfaceEventHandlers event_handlers = {};
+#ifndef WIFI_SKIP_STATE_TOGGLE_OFF_ON_FOR_NAN
event_handlers.on_state_toggle_off_on = [weak_ptr_this](const std::string& /* iface_name */) {
const auto shared_ptr_this = weak_ptr_this.promote();
if (!shared_ptr_this.get() || !shared_ptr_this->isValid()) {
@@ -467,6 +468,7 @@
}
}
};
+#endif
iface_util_.lock()->registerIfaceEventHandlers(ifname_, event_handlers);
}
diff --git a/wifi/aidl/default/Android.bp b/wifi/aidl/default/Android.bp
index 441d461..91d609d 100644
--- a/wifi/aidl/default/Android.bp
+++ b/wifi/aidl/default/Android.bp
@@ -26,6 +26,7 @@
"hidl_feature_disable_ap", // WIFI_HIDL_FEATURE_DISABLE_AP
"hidl_feature_disable_ap_mac_randomization", // WIFI_HIDL_FEATURE_DISABLE_AP_MAC_RANDOMIZATION
"avoid_iface_reset_mac_change", // WIFI_AVOID_IFACE_RESET_MAC_CHANGE
+ "wifi_skip_state_toggle_off_on_for_nan", // WIFI_SKIP_STATE_TOGGLE_OFF_ON_FOR_NAN
],
value_variables: [
"hal_interface_combinations", // WIFI_HAL_INTERFACE_COMBINATIONS
@@ -53,6 +54,9 @@
avoid_iface_reset_mac_change: {
cppflags: ["-DWIFI_AVOID_IFACE_RESET_MAC_CHANGE"],
},
+ wifi_skip_state_toggle_off_on_for_nan: {
+ cppflags: ["-DWIFI_SKIP_STATE_TOGGLE_OFF_ON_FOR_NAN"],
+ },
hal_interface_combinations: {
cppflags: ["-DWIFI_HAL_INTERFACE_COMBINATIONS=%s"],
},
diff --git a/wifi/aidl/default/wifi_nan_iface.cpp b/wifi/aidl/default/wifi_nan_iface.cpp
index 8e3a191..cefe7f7 100644
--- a/wifi/aidl/default/wifi_nan_iface.cpp
+++ b/wifi/aidl/default/wifi_nan_iface.cpp
@@ -623,6 +623,7 @@
// Register for iface state toggle events.
iface_util::IfaceEventHandlers event_handlers = {};
+#ifndef WIFI_SKIP_STATE_TOGGLE_OFF_ON_FOR_NAN
event_handlers.on_state_toggle_off_on = [weak_ptr_this](const std::string& /* iface_name */) {
const auto shared_ptr_this = weak_ptr_this.lock();
if (!shared_ptr_this.get() || !shared_ptr_this->isValid()) {
@@ -637,6 +638,7 @@
}
}
};
+#endif
iface_util_.lock()->registerIfaceEventHandlers(ifname_, event_handlers);
}
diff --git a/wifi/aidl/vts/functional/wifi_sta_iface_aidl_test.cpp b/wifi/aidl/vts/functional/wifi_sta_iface_aidl_test.cpp
index 07817cc..adc9b73 100644
--- a/wifi/aidl/vts/functional/wifi_sta_iface_aidl_test.cpp
+++ b/wifi/aidl/vts/functional/wifi_sta_iface_aidl_test.cpp
@@ -92,14 +92,21 @@
}
/*
- * GetApfPacketFilterCapabilities
+ * CheckApfIsSupported:
+ * Ensures the APF packet filter is fully supported as required in VSR 14:
+ * https://docs.partner.android.com/gms/policies/vsr/vsr-14
*/
-TEST_P(WifiStaIfaceAidlTest, GetApfPacketFilterCapabilities) {
- if (!isFeatureSupported(IWifiStaIface::FeatureSetMask::APF)) {
- GTEST_SKIP() << "APF packet filter capabilities are not supported.";
- }
+TEST_P(WifiStaIfaceAidlTest, CheckApfIsSupported) {
+ // It is not required to check the vendor API level is at least U here
+ // because the Wi-Fi AIDL interface is launched with Android U(VSR-14).
+ // TODO: Add wavier list to the chipsets that doesn't support APF.
+ EXPECT_TRUE(isFeatureSupported(IWifiStaIface::FeatureSetMask::APF));
StaApfPacketFilterCapabilities apf_caps = {};
EXPECT_TRUE(wifi_sta_iface_->getApfPacketFilterCapabilities(&apf_caps).isOk());
+ // The APF version must be 4 and the usable memory must be at least
+ // 1024 bytes.
+ EXPECT_EQ(apf_caps.version, 4);
+ EXPECT_GE(apf_caps.maxLength, 1024);
}
/*
diff --git a/wifi/supplicant/aidl/android/hardware/wifi/supplicant/INonStandardCertCallback.aidl b/wifi/supplicant/aidl/android/hardware/wifi/supplicant/INonStandardCertCallback.aidl
index 2f9e528..2e11c76 100644
--- a/wifi/supplicant/aidl/android/hardware/wifi/supplicant/INonStandardCertCallback.aidl
+++ b/wifi/supplicant/aidl/android/hardware/wifi/supplicant/INonStandardCertCallback.aidl
@@ -18,7 +18,8 @@
/**
* Callback to allow supplicant to retrieve non-standard certificate types
- * from the client.
+ * from the framework. Certificates can be stored in the framework using
+ * the WifiKeystore#put system API.
*
* Must be registered by the client at initialization, so that
* supplicant can call into the client to retrieve any values.