Merge "Add DISABLED_OPTIONAL_FEATURES property"
diff --git a/audio/6.0/IDevice.hal b/audio/6.0/IDevice.hal
index 520e776..122c550 100644
--- a/audio/6.0/IDevice.hal
+++ b/audio/6.0/IDevice.hal
@@ -286,8 +286,12 @@
* all currently allocated resources. It is recommended to close
* the device on the client side as soon as it is becomes unused.
*
+ * Note that all streams must be closed by the client before
+ * attempting to close the device they belong to.
+ *
* @return retval OK in case the success.
- * INVALID_STATE if the device was already closed.
+ * INVALID_STATE if the device was already closed
+ * or there are streams currently opened.
*/
@exit
close() generates (Result retval);
diff --git a/audio/core/all-versions/default/Device.cpp b/audio/core/all-versions/default/Device.cpp
index 5ea4c8d..21dab00 100644
--- a/audio/core/all-versions/default/Device.cpp
+++ b/audio/core/all-versions/default/Device.cpp
@@ -53,10 +53,14 @@
void Device::closeInputStream(audio_stream_in_t* stream) {
mDevice->close_input_stream(mDevice, stream);
+ LOG_ALWAYS_FATAL_IF(mOpenedStreamsCount == 0, "mOpenedStreamsCount is already 0");
+ --mOpenedStreamsCount;
}
void Device::closeOutputStream(audio_stream_out_t* stream) {
mDevice->close_output_stream(mDevice, stream);
+ LOG_ALWAYS_FATAL_IF(mOpenedStreamsCount == 0, "mOpenedStreamsCount is already 0");
+ --mOpenedStreamsCount;
}
char* Device::halGetParameters(const char* keys) {
@@ -158,6 +162,7 @@
sp<IStreamOut> streamOut;
if (status == OK) {
streamOut = new StreamOut(this, halStream);
+ ++mOpenedStreamsCount;
}
HidlUtils::audioConfigFromHal(halConfig, suggestedConfig);
return {analyzeStatus("open_output_stream", status, {EINVAL} /*ignore*/), streamOut};
@@ -184,6 +189,7 @@
sp<IStreamIn> streamIn;
if (status == OK) {
streamIn = new StreamIn(this, halStream);
+ ++mOpenedStreamsCount;
}
HidlUtils::audioConfigFromHal(halConfig, suggestedConfig);
return {analyzeStatus("open_input_stream", status, {EINVAL} /*ignore*/), streamIn};
@@ -383,7 +389,7 @@
#endif
Result Device::doClose() {
- if (mIsClosed) return Result::INVALID_STATE;
+ if (mIsClosed || mOpenedStreamsCount != 0) return Result::INVALID_STATE;
mIsClosed = true;
return analyzeStatus("close", audio_hw_device_close(mDevice));
}
diff --git a/audio/core/all-versions/default/include/core/default/Device.h b/audio/core/all-versions/default/include/core/default/Device.h
index dc53a39..11ab607 100644
--- a/audio/core/all-versions/default/include/core/default/Device.h
+++ b/audio/core/all-versions/default/include/core/default/Device.h
@@ -130,6 +130,7 @@
private:
bool mIsClosed;
audio_hw_device_t* mDevice;
+ int mOpenedStreamsCount = 0;
virtual ~Device();
diff --git a/audio/core/all-versions/vts/functional/4.0/AudioPrimaryHidlHalTest.cpp b/audio/core/all-versions/vts/functional/4.0/AudioPrimaryHidlHalTest.cpp
index 81f963d..709b7cd 100644
--- a/audio/core/all-versions/vts/functional/4.0/AudioPrimaryHidlHalTest.cpp
+++ b/audio/core/all-versions/vts/functional/4.0/AudioPrimaryHidlHalTest.cpp
@@ -52,7 +52,6 @@
doc::test(
"Make sure getMicrophones always succeeds"
"and getActiveMicrophones always succeeds when recording from these microphones.");
- AudioIoHandle ioHandle = (AudioIoHandle)AudioHandleConsts::AUDIO_IO_HANDLE_NONE;
AudioConfig config{};
config.channelMask = mkEnumBitfield(AudioChannelMask::IN_MONO);
config.sampleRateHz = 8000;
@@ -65,18 +64,14 @@
continue;
}
sp<IStreamIn> stream;
+ StreamHelper<IStreamIn> helper(stream);
AudioConfig suggestedConfig{};
- ASSERT_OK(getDevice()->openInputStream(ioHandle, microphone.deviceAddress, config,
- flags, initMetadata,
- returnIn(res, stream, suggestedConfig)));
- if (res != Result::OK) {
- ASSERT_TRUE(stream == nullptr);
- AudioConfig suggestedConfigRetry{};
- ASSERT_OK(getDevice()->openInputStream(
- ioHandle, microphone.deviceAddress, suggestedConfig, flags, initMetadata,
- returnIn(res, stream, suggestedConfigRetry)));
- }
- ASSERT_OK(res);
+ ASSERT_NO_FATAL_FAILURE(helper.open(
+ [&](AudioIoHandle handle, AudioConfig config, auto cb) {
+ return getDevice()->openInputStream(handle, microphone.deviceAddress,
+ config, flags, initMetadata, cb);
+ },
+ config, &res, &suggestedConfig));
hidl_vec<MicrophoneInfo> activeMicrophones;
Result readRes;
typedef MessageQueue<IStreamIn::ReadParameters, kSynchronizedReadWrite> CommandMQ;
@@ -110,13 +105,8 @@
ASSERT_OK(res);
ASSERT_NE(0U, activeMicrophones.size());
}
- stream->close();
-#if MAJOR_VERSION <= 5
- // Workaround for b/139329877. Ensures the stream gets closed on the audio hal side.
- stream.clear();
- IPCThreadState::self()->flushCommands();
- usleep(1000);
-#endif
+ helper.close(true /*clear*/, &res);
+ ASSERT_OK(res);
if (efGroup) {
EventFlag::deleteEventFlag(&efGroup);
}
diff --git a/audio/core/all-versions/vts/functional/6.0/AudioPrimaryHidlHalTest.cpp b/audio/core/all-versions/vts/functional/6.0/AudioPrimaryHidlHalTest.cpp
index 7e931ff..22e60be 100644
--- a/audio/core/all-versions/vts/functional/6.0/AudioPrimaryHidlHalTest.cpp
+++ b/audio/core/all-versions/vts/functional/6.0/AudioPrimaryHidlHalTest.cpp
@@ -145,3 +145,49 @@
}();
return parameters;
}
+
+TEST_P(AudioHidlDeviceTest, CloseDeviceWithOpenedOutputStreams) {
+ doc::test("Verify that a device can't be closed if there are streams opened");
+ DeviceAddress address{.device = AudioDevice::OUT_DEFAULT};
+ AudioConfig config{};
+ auto flags = hidl_bitfield<AudioOutputFlag>(AudioOutputFlag::NONE);
+ SourceMetadata initMetadata = {{{AudioUsage::MEDIA, AudioContentType::MUSIC, 1 /* gain */}}};
+ sp<IStreamOut> stream;
+ StreamHelper<IStreamOut> helper(stream);
+ AudioConfig suggestedConfig{};
+ ASSERT_NO_FATAL_FAILURE(helper.open(
+ [&](AudioIoHandle handle, AudioConfig config, auto cb) {
+ return getDevice()->openOutputStream(handle, address, config, flags, initMetadata,
+ cb);
+ },
+ config, &res, &suggestedConfig));
+ ASSERT_RESULT(Result::INVALID_STATE, getDevice()->close());
+ ASSERT_NO_FATAL_FAILURE(helper.close(true /*clear*/, &res));
+ ASSERT_OK(getDevice()->close());
+ ASSERT_TRUE(resetDevice());
+}
+
+TEST_P(AudioHidlDeviceTest, CloseDeviceWithOpenedInputStreams) {
+ doc::test("Verify that a device can't be closed if there are streams opened");
+ auto module = getCachedPolicyConfig().getModuleFromName(getDeviceName());
+ if (module->getInputProfiles().empty()) {
+ GTEST_SKIP() << "Device doesn't have input profiles";
+ }
+ DeviceAddress address{.device = AudioDevice::IN_DEFAULT};
+ AudioConfig config{};
+ auto flags = hidl_bitfield<AudioInputFlag>(AudioInputFlag::NONE);
+ SinkMetadata initMetadata = {{{.source = AudioSource::MIC, .gain = 1}}};
+ sp<IStreamIn> stream;
+ StreamHelper<IStreamIn> helper(stream);
+ AudioConfig suggestedConfig{};
+ ASSERT_NO_FATAL_FAILURE(helper.open(
+ [&](AudioIoHandle handle, AudioConfig config, auto cb) {
+ return getDevice()->openInputStream(handle, address, config, flags, initMetadata,
+ cb);
+ },
+ config, &res, &suggestedConfig));
+ ASSERT_RESULT(Result::INVALID_STATE, getDevice()->close());
+ ASSERT_NO_FATAL_FAILURE(helper.close(true /*clear*/, &res));
+ ASSERT_OK(getDevice()->close());
+ ASSERT_TRUE(resetDevice());
+}
diff --git a/audio/core/all-versions/vts/functional/AudioPrimaryHidlHalTest.h b/audio/core/all-versions/vts/functional/AudioPrimaryHidlHalTest.h
index 6fc9339..d0d39e8 100644
--- a/audio/core/all-versions/vts/functional/AudioPrimaryHidlHalTest.h
+++ b/audio/core/all-versions/vts/functional/AudioPrimaryHidlHalTest.h
@@ -718,12 +718,6 @@
::testing::Values(AudioInputFlag::NONE)),
&DeviceConfigParameterToString);
INSTANTIATE_TEST_CASE_P(
- SupportedInputBufferSize, RequiredInputBufferSizeTest,
- ::testing::Combine(::testing::ValuesIn(getDeviceParameters()),
- ::testing::ValuesIn(ConfigHelper::getSupportedCaptureAudioConfig()),
- ::testing::Values(AudioInputFlag::NONE)),
- &DeviceConfigParameterToString);
-INSTANTIATE_TEST_CASE_P(
RecommendedCaptureAudioConfigSupport, OptionalInputBufferSizeTest,
::testing::Combine(
::testing::ValuesIn(getDeviceParametersForPrimaryDeviceTests()),
@@ -815,62 +809,84 @@
////////////////////////// open{Output,Input}Stream //////////////////////////
//////////////////////////////////////////////////////////////////////////////
+// This class is also used by some device tests.
template <class Stream>
-class OpenStreamTest : public AudioHidlTestWithDeviceConfigParameter {
- protected:
+class StreamHelper {
+ public:
+ // StreamHelper doesn't own the stream, this is for simpler stream lifetime management.
+ explicit StreamHelper(sp<Stream>& stream) : mStream(stream) {}
template <class Open>
- void testOpen(Open openStream, const AudioConfig& config) {
+ void open(Open openStream, const AudioConfig& config, Result* res,
+ AudioConfig* suggestedConfigPtr) {
// FIXME: Open a stream without an IOHandle
// This is not required to be accepted by hal implementations
AudioIoHandle ioHandle = (AudioIoHandle)AudioHandleConsts::AUDIO_IO_HANDLE_NONE;
AudioConfig suggestedConfig{};
- ASSERT_OK(openStream(ioHandle, config, returnIn(res, stream, suggestedConfig)));
-
- // TODO: only allow failure for RecommendedPlaybackAudioConfig
- switch (res) {
+ bool retryWithSuggestedConfig = true;
+ if (suggestedConfigPtr == nullptr) {
+ suggestedConfigPtr = &suggestedConfig;
+ retryWithSuggestedConfig = false;
+ }
+ ASSERT_OK(openStream(ioHandle, config, returnIn(*res, mStream, *suggestedConfigPtr)));
+ switch (*res) {
case Result::OK:
- ASSERT_TRUE(stream != nullptr);
- audioConfig = config;
+ ASSERT_TRUE(mStream != nullptr);
+ *suggestedConfigPtr = config;
break;
case Result::INVALID_ARGUMENTS:
- ASSERT_TRUE(stream == nullptr);
- AudioConfig suggestedConfigRetry;
- // Could not open stream with config, try again with the
- // suggested one
- ASSERT_OK(openStream(ioHandle, suggestedConfig,
- returnIn(res, stream, suggestedConfigRetry)));
- // This time it must succeed
- ASSERT_OK(res);
- ASSERT_TRUE(stream != nullptr);
- audioConfig = suggestedConfig;
+ ASSERT_TRUE(mStream == nullptr);
+ if (retryWithSuggestedConfig) {
+ AudioConfig suggestedConfigRetry;
+ ASSERT_OK(openStream(ioHandle, *suggestedConfigPtr,
+ returnIn(*res, mStream, suggestedConfigRetry)));
+ ASSERT_OK(*res);
+ ASSERT_TRUE(mStream != nullptr);
+ }
break;
default:
- FAIL() << "Invalid return status: " << ::testing::PrintToString(res);
+ FAIL() << "Invalid return status: " << ::testing::PrintToString(*res);
}
+ }
+ void close(bool clear, Result* res) {
+ auto ret = mStream->close();
+ EXPECT_TRUE(ret.isOk());
+ *res = ret;
+ if (clear) {
+ mStream.clear();
+#if MAJOR_VERSION <= 5
+ // FIXME: there is no way to know when the remote IStream is being destroyed
+ // Binder does not support testing if an object is alive, thus
+ // wait for 100ms to let the binder destruction propagates and
+ // the remote device has the time to be destroyed.
+ // flushCommand makes sure all local command are sent, thus should reduce
+ // the latency between local and remote destruction.
+ IPCThreadState::self()->flushCommands();
+ usleep(100 * 1000);
+#endif
+ }
+ }
+
+ private:
+ sp<Stream>& mStream;
+};
+
+template <class Stream>
+class OpenStreamTest : public AudioHidlTestWithDeviceConfigParameter {
+ protected:
+ OpenStreamTest() : AudioHidlTestWithDeviceConfigParameter(), helper(stream) {}
+ template <class Open>
+ void testOpen(Open openStream, const AudioConfig& config) {
+ // TODO: only allow failure for RecommendedPlaybackAudioConfig
+ ASSERT_NO_FATAL_FAILURE(helper.open(openStream, config, &res, &audioConfig));
open = true;
}
- Return<Result> closeStream() {
+ Result closeStream(bool clear = true) {
open = false;
- auto res = stream->close();
- stream.clear();
- waitForStreamDestruction();
+ helper.close(clear, &res);
return res;
}
- static void waitForStreamDestruction() {
-#if MAJOR_VERSION <= 5
- // FIXME: there is no way to know when the remote IStream is being destroyed
- // Binder does not support testing if an object is alive, thus
- // wait for 100ms to let the binder destruction propagates and
- // the remote device has the time to be destroyed.
- // flushCommand makes sure all local command are sent, thus should reduce
- // the latency between local and remote destruction.
- IPCThreadState::self()->flushCommands();
- usleep(100 * 1000);
-#endif
- }
-
private:
void TearDown() override {
if (open) {
@@ -883,6 +899,7 @@
AudioConfig audioConfig;
DeviceAddress address = {};
sp<Stream> stream;
+ StreamHelper<Stream> helper;
bool open = false;
};
@@ -931,12 +948,6 @@
::testing::Values(AudioOutputFlag::NONE)),
&DeviceConfigParameterToString);
INSTANTIATE_TEST_CASE_P(
- SupportedOutputStreamConfig, OutputStreamTest,
- ::testing::Combine(::testing::ValuesIn(getDeviceParameters()),
- ::testing::ValuesIn(ConfigHelper::getSupportedPlaybackAudioConfig()),
- ::testing::Values(AudioOutputFlag::NONE)),
- &DeviceConfigParameterToString);
-INSTANTIATE_TEST_CASE_P(
RecommendedOutputStreamConfigSupport, OutputStreamTest,
::testing::Combine(
::testing::ValuesIn(getDeviceParametersForPrimaryDeviceTests()),
@@ -946,7 +957,7 @@
#elif MAJOR_VERSION >= 6
// For V6 and above test according to the audio policy manager configuration.
// This is more correct as CDD is written from the apps perspective.
-// Audio system provides necessary format conversions for the missing configurations.
+// Audio system provides necessary format conversions for missing configurations.
INSTANTIATE_TEST_CASE_P(DeclaredOutputStreamConfigSupport, OutputStreamTest,
::testing::ValuesIn(getOutputDeviceConfigParameters()),
&DeviceConfigParameterToString);
@@ -992,12 +1003,6 @@
::testing::Values(AudioInputFlag::NONE)),
&DeviceConfigParameterToString);
INSTANTIATE_TEST_CASE_P(
- SupportedInputStreamConfig, InputStreamTest,
- ::testing::Combine(::testing::ValuesIn(getDeviceParameters()),
- ::testing::ValuesIn(ConfigHelper::getSupportedCaptureAudioConfig()),
- ::testing::Values(AudioInputFlag::NONE)),
- &DeviceConfigParameterToString);
-INSTANTIATE_TEST_CASE_P(
RecommendedInputStreamConfigSupport, InputStreamTest,
::testing::Combine(
::testing::ValuesIn(getDeviceParametersForPrimaryDeviceTests()),
@@ -1007,7 +1012,7 @@
#elif MAJOR_VERSION >= 6
// For V6 and above test according to the audio policy manager configuration.
// This is more correct as CDD is written from the apps perspective.
-// Audio system provides necessary format conversions for the missing configurations.
+// Audio system provides necessary format conversions for missing configurations.
INSTANTIATE_TEST_CASE_P(DeclaredInputStreamConfigSupport, InputStreamTest,
::testing::ValuesIn(getInputDeviceConfigParameters()),
&DeviceConfigParameterToString);
@@ -1196,11 +1201,8 @@
TEST_IO_STREAM(close, "Make sure a stream can be closed", ASSERT_OK(closeStream()))
// clang-format off
TEST_IO_STREAM(closeTwice, "Make sure a stream can not be closed twice",
- auto streamCopy = stream;
- ASSERT_OK(closeStream());
- ASSERT_RESULT(Result::INVALID_STATE, streamCopy->close());
- streamCopy.clear();
- waitForStreamDestruction())
+ ASSERT_OK(closeStream(false /*clear*/));
+ ASSERT_EQ(Result::INVALID_STATE, closeStream()))
// clang-format on
static void testMmapBufferOfInvalidSize(IStream* stream) {
diff --git a/audio/core/all-versions/vts/functional/ConfigHelper.h b/audio/core/all-versions/vts/functional/ConfigHelper.h
index 48aae8c..a2f4116 100644
--- a/audio/core/all-versions/vts/functional/ConfigHelper.h
+++ b/audio/core/all-versions/vts/functional/ConfigHelper.h
@@ -57,12 +57,6 @@
{24000, 48000}, {AudioFormat::PCM_16_BIT});
}
- static const vector<AudioConfig> getSupportedPlaybackAudioConfig() {
- // TODO: retrieve audio config supported by the platform
- // as declared in the policy configuration
- return {};
- }
-
static const vector<AudioConfig> getRequiredSupportCaptureAudioConfig() {
if (!primaryHasMic()) return {};
return combineAudioConfig({AudioChannelMask::IN_MONO}, {8000, 11025, 16000, 44100},
@@ -73,11 +67,6 @@
return combineAudioConfig({AudioChannelMask::IN_STEREO}, {22050, 48000},
{AudioFormat::PCM_16_BIT});
}
- static const vector<AudioConfig> getSupportedCaptureAudioConfig() {
- // TODO: retrieve audio config supported by the platform
- // as declared in the policy configuration
- return {};
- }
static vector<AudioConfig> combineAudioConfig(vector<audio_channel_mask_t> channelMasks,
vector<uint32_t> sampleRates,
diff --git a/audio/effect/all-versions/default/Effect.cpp b/audio/effect/all-versions/default/Effect.cpp
index 0afa779..e11e123 100644
--- a/audio/effect/all-versions/default/Effect.cpp
+++ b/audio/effect/all-versions/default/Effect.cpp
@@ -307,12 +307,11 @@
Result Effect::getCurrentConfigImpl(uint32_t featureId, uint32_t configSize,
GetCurrentConfigSuccessCallback onSuccess) {
uint32_t halCmd = featureId;
- uint32_t halResult[alignedSizeIn<uint32_t>(sizeof(uint32_t) + configSize)];
- memset(halResult, 0, sizeof(halResult));
+ std::vector<uint32_t> halResult(alignedSizeIn<uint32_t>(sizeof(uint32_t) + configSize), 0);
uint32_t halResultSize = 0;
- return sendCommandReturningStatusAndData(EFFECT_CMD_GET_FEATURE_CONFIG, "GET_FEATURE_CONFIG",
- sizeof(uint32_t), &halCmd, &halResultSize, halResult,
- sizeof(uint32_t), [&] { onSuccess(&halResult[1]); });
+ return sendCommandReturningStatusAndData(
+ EFFECT_CMD_GET_FEATURE_CONFIG, "GET_FEATURE_CONFIG", sizeof(uint32_t), &halCmd,
+ &halResultSize, &halResult[0], sizeof(uint32_t), [&] { onSuccess(&halResult[1]); });
}
Result Effect::getParameterImpl(uint32_t paramSize, const void* paramData,
@@ -339,8 +338,7 @@
GetSupportedConfigsSuccessCallback onSuccess) {
uint32_t halCmd[2] = {featureId, maxConfigs};
uint32_t halResultSize = 2 * sizeof(uint32_t) + maxConfigs * sizeof(configSize);
- uint8_t halResult[halResultSize];
- memset(&halResult[0], 0, halResultSize);
+ std::vector<uint8_t> halResult(static_cast<size_t>(halResultSize), 0);
return sendCommandReturningStatusAndData(
EFFECT_CMD_GET_FEATURE_SUPPORTED_CONFIGS, "GET_FEATURE_SUPPORTED_CONFIGS", sizeof(halCmd),
halCmd, &halResultSize, &halResult[0], 2 * sizeof(uint32_t), [&] {
@@ -519,9 +517,9 @@
uint32_t halDataSize;
std::unique_ptr<uint8_t[]> halData = hidlVecToHal(volumes, &halDataSize);
uint32_t halResultSize = halDataSize;
- uint32_t halResult[volumes.size()];
+ std::vector<uint32_t> halResult(volumes.size(), 0);
Result retval = sendCommandReturningData(EFFECT_CMD_SET_VOLUME, "SET_VOLUME", halDataSize,
- &halData[0], &halResultSize, halResult);
+ &halData[0], &halResultSize, &halResult[0]);
hidl_vec<uint32_t> result;
if (retval == Result::OK) {
result.setToExternal(&halResult[0], halResultSize);
@@ -581,8 +579,6 @@
}
Return<void> Effect::getAuxChannelsConfig(getAuxChannelsConfig_cb _hidl_cb) {
- uint32_t halResult[alignedSizeIn<uint32_t>(sizeof(uint32_t) + sizeof(channel_config_t))];
- memset(halResult, 0, sizeof(halResult));
EffectAuxChannelsConfig result;
Result retval = getCurrentConfigImpl(
EFFECT_FEATURE_AUX_CHANNELS, sizeof(channel_config_t), [&](void* configData) {
@@ -594,11 +590,12 @@
}
Return<Result> Effect::setAuxChannelsConfig(const EffectAuxChannelsConfig& config) {
- uint32_t halCmd[alignedSizeIn<uint32_t>(sizeof(uint32_t) + sizeof(channel_config_t))];
+ std::vector<uint32_t> halCmd(
+ alignedSizeIn<uint32_t>(sizeof(uint32_t) + sizeof(channel_config_t)), 0);
halCmd[0] = EFFECT_FEATURE_AUX_CHANNELS;
effectAuxChannelsConfigToHal(config, reinterpret_cast<channel_config_t*>(&halCmd[1]));
return sendCommandReturningStatus(EFFECT_CMD_SET_FEATURE_CONFIG,
- "SET_FEATURE_CONFIG AUX_CHANNELS", sizeof(halCmd), halCmd);
+ "SET_FEATURE_CONFIG AUX_CHANNELS", halCmd.size(), &halCmd[0]);
}
Return<Result> Effect::setAudioSource(AudioSource source) {
@@ -692,12 +689,11 @@
Return<Result> Effect::setCurrentConfigForFeature(uint32_t featureId,
const hidl_vec<uint8_t>& configData) {
- uint32_t halCmd[alignedSizeIn<uint32_t>(sizeof(uint32_t) + configData.size())];
- memset(halCmd, 0, sizeof(halCmd));
+ std::vector<uint32_t> halCmd(alignedSizeIn<uint32_t>(sizeof(uint32_t) + configData.size()), 0);
halCmd[0] = featureId;
memcpy(&halCmd[1], &configData[0], configData.size());
return sendCommandReturningStatus(EFFECT_CMD_SET_FEATURE_CONFIG, "SET_FEATURE_CONFIG",
- sizeof(halCmd), halCmd);
+ halCmd.size(), &halCmd[0]);
}
Return<Result> Effect::close() {
diff --git a/automotive/vehicle/2.0/default/Android.bp b/automotive/vehicle/2.0/default/Android.bp
index ed09859..f9c25d1 100644
--- a/automotive/vehicle/2.0/default/Android.bp
+++ b/automotive/vehicle/2.0/default/Android.bp
@@ -58,6 +58,7 @@
defaults: ["vhal_v2_0_defaults"],
srcs: [
"impl/vhal_v2_0/CommConn.cpp",
+ "impl/vhal_v2_0/EmulatedVehicleConnector.cpp",
"impl/vhal_v2_0/EmulatedVehicleHal.cpp",
"impl/vhal_v2_0/VehicleEmulator.cpp",
"impl/vhal_v2_0/PipeComm.cpp",
diff --git a/automotive/vehicle/2.0/default/VehicleService.cpp b/automotive/vehicle/2.0/default/VehicleService.cpp
index d1fd555..127eb98 100644
--- a/automotive/vehicle/2.0/default/VehicleService.cpp
+++ b/automotive/vehicle/2.0/default/VehicleService.cpp
@@ -20,8 +20,9 @@
#include <iostream>
-#include <vhal_v2_0/VehicleHalManager.h>
+#include <vhal_v2_0/EmulatedVehicleConnector.h>
#include <vhal_v2_0/EmulatedVehicleHal.h>
+#include <vhal_v2_0/VehicleHalManager.h>
using namespace android;
using namespace android::hardware;
@@ -29,9 +30,11 @@
int main(int /* argc */, char* /* argv */ []) {
auto store = std::make_unique<VehiclePropertyStore>();
- auto hal = std::make_unique<impl::EmulatedVehicleHal>(store.get());
+ auto connector = impl::makeEmulatedPassthroughConnector();
+ auto hal = std::make_unique<impl::EmulatedVehicleHal>(store.get(), connector.get());
auto emulator = std::make_unique<impl::VehicleEmulator>(hal.get());
auto service = std::make_unique<VehicleHalManager>(hal.get());
+ connector->setValuePool(hal->getValuePool());
configureRpcThreadpool(4, true /* callerWillJoin */);
diff --git a/automotive/vehicle/2.0/default/impl/vhal_v2_0/EmulatedVehicleConnector.cpp b/automotive/vehicle/2.0/default/impl/vhal_v2_0/EmulatedVehicleConnector.cpp
new file mode 100644
index 0000000..168999d
--- /dev/null
+++ b/automotive/vehicle/2.0/default/impl/vhal_v2_0/EmulatedVehicleConnector.cpp
@@ -0,0 +1,254 @@
+/*
+ * 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.
+ */
+#include <android-base/logging.h>
+#include <utils/SystemClock.h>
+
+#include "DefaultConfig.h"
+#include "EmulatedVehicleConnector.h"
+#include "JsonFakeValueGenerator.h"
+#include "LinearFakeValueGenerator.h"
+#include "Obd2SensorStore.h"
+
+namespace android {
+namespace hardware {
+namespace automotive {
+namespace vehicle {
+namespace V2_0 {
+
+namespace impl {
+
+void EmulatedVehicleClient::onPropertyValue(const VehiclePropValue& value) {
+ if (!mPropCallback) {
+ LOG(ERROR) << __func__ << ": PropertyCallBackType is not registered!";
+ return;
+ }
+ return mPropCallback(value);
+}
+
+void EmulatedVehicleClient::registerPropertyValueCallback(PropertyCallBackType&& callback) {
+ if (mPropCallback) {
+ LOG(ERROR) << __func__ << ": Cannot register multiple callbacks!";
+ return;
+ }
+ mPropCallback = std::move(callback);
+}
+
+GeneratorHub* EmulatedVehicleServer::getGenerator() {
+ return &mGeneratorHub;
+}
+
+VehiclePropValuePool* EmulatedVehicleServer::getValuePool() const {
+ if (!mValuePool) {
+ LOG(WARNING) << __func__ << ": Value pool not set!";
+ }
+ return mValuePool;
+}
+
+void EmulatedVehicleServer::setValuePool(VehiclePropValuePool* valuePool) {
+ if (!valuePool) {
+ LOG(WARNING) << __func__ << ": Setting value pool to nullptr!";
+ }
+ mValuePool = valuePool;
+}
+
+void EmulatedVehicleServer::onFakeValueGenerated(const VehiclePropValue& value) {
+ LOG(DEBUG) << __func__ << ": " << toString(value);
+ auto updatedPropValue = getValuePool()->obtain(value);
+ if (updatedPropValue) {
+ updatedPropValue->timestamp = value.timestamp;
+ updatedPropValue->status = VehiclePropertyStatus::AVAILABLE;
+ onPropertyValueFromCar(*updatedPropValue);
+ }
+}
+
+std::vector<VehiclePropConfig> EmulatedVehicleServer::onGetAllPropertyConfig() const {
+ std::vector<VehiclePropConfig> vehiclePropConfigs;
+ constexpr size_t numOfVehiclePropConfigs =
+ sizeof(kVehicleProperties) / sizeof(kVehicleProperties[0]);
+ vehiclePropConfigs.reserve(numOfVehiclePropConfigs);
+ for (auto& it : kVehicleProperties) {
+ vehiclePropConfigs.emplace_back(it.config);
+ }
+ return vehiclePropConfigs;
+}
+
+StatusCode EmulatedVehicleServer::handleGenerateFakeDataRequest(const VehiclePropValue& request) {
+ LOG(INFO) << __func__;
+ const auto& v = request.value;
+ if (!v.int32Values.size()) {
+ LOG(ERROR) << __func__ << ": expected at least \"command\" field in int32Values";
+ return StatusCode::INVALID_ARG;
+ }
+
+ FakeDataCommand command = static_cast<FakeDataCommand>(v.int32Values[0]);
+
+ switch (command) {
+ case FakeDataCommand::StartLinear: {
+ LOG(INFO) << __func__ << ", FakeDataCommand::StartLinear";
+ if (v.int32Values.size() < 2) {
+ LOG(ERROR) << __func__ << ": expected property ID in int32Values";
+ return StatusCode::INVALID_ARG;
+ }
+ if (!v.int64Values.size()) {
+ LOG(ERROR) << __func__ << ": interval is not provided in int64Values";
+ return StatusCode::INVALID_ARG;
+ }
+ if (v.floatValues.size() < 3) {
+ LOG(ERROR) << __func__ << ": expected at least 3 elements in floatValues, got: "
+ << v.floatValues.size();
+ return StatusCode::INVALID_ARG;
+ }
+ int32_t cookie = v.int32Values[1];
+ getGenerator()->registerGenerator(cookie,
+ std::make_unique<LinearFakeValueGenerator>(request));
+ break;
+ }
+ case FakeDataCommand::StartJson: {
+ LOG(INFO) << __func__ << ", FakeDataCommand::StartJson";
+ if (v.stringValue.empty()) {
+ LOG(ERROR) << __func__ << ": path to JSON file is missing";
+ return StatusCode::INVALID_ARG;
+ }
+ int32_t cookie = std::hash<std::string>()(v.stringValue);
+ getGenerator()->registerGenerator(cookie,
+ std::make_unique<JsonFakeValueGenerator>(request));
+ break;
+ }
+ case FakeDataCommand::StopLinear: {
+ LOG(INFO) << __func__ << ", FakeDataCommand::StopLinear";
+ if (v.int32Values.size() < 2) {
+ LOG(ERROR) << __func__ << ": expected property ID in int32Values";
+ return StatusCode::INVALID_ARG;
+ }
+ int32_t cookie = v.int32Values[1];
+ getGenerator()->unregisterGenerator(cookie);
+ break;
+ }
+ case FakeDataCommand::StopJson: {
+ LOG(INFO) << __func__ << ", FakeDataCommand::StopJson";
+ if (v.stringValue.empty()) {
+ LOG(ERROR) << __func__ << ": path to JSON file is missing";
+ return StatusCode::INVALID_ARG;
+ }
+ int32_t cookie = std::hash<std::string>()(v.stringValue);
+ getGenerator()->unregisterGenerator(cookie);
+ break;
+ }
+ case FakeDataCommand::KeyPress: {
+ LOG(INFO) << __func__ << ", FakeDataCommand::KeyPress";
+ int32_t keyCode = request.value.int32Values[2];
+ int32_t display = request.value.int32Values[3];
+ // Send back to HAL
+ onPropertyValueFromCar(
+ *createHwInputKeyProp(VehicleHwKeyInputAction::ACTION_DOWN, keyCode, display));
+ onPropertyValueFromCar(
+ *createHwInputKeyProp(VehicleHwKeyInputAction::ACTION_UP, keyCode, display));
+ break;
+ }
+ default: {
+ LOG(ERROR) << __func__ << ": unexpected command: " << toInt(command);
+ return StatusCode::INVALID_ARG;
+ }
+ }
+ return StatusCode::OK;
+}
+
+VehicleHal::VehiclePropValuePtr EmulatedVehicleServer::createApPowerStateReq(
+ VehicleApPowerStateReq state, int32_t param) {
+ auto req = getValuePool()->obtain(VehiclePropertyType::INT32_VEC, 2);
+ req->prop = toInt(VehicleProperty::AP_POWER_STATE_REQ);
+ req->areaId = 0;
+ req->timestamp = elapsedRealtimeNano();
+ req->status = VehiclePropertyStatus::AVAILABLE;
+ req->value.int32Values[0] = toInt(state);
+ req->value.int32Values[1] = param;
+ return req;
+}
+
+VehicleHal::VehiclePropValuePtr EmulatedVehicleServer::createHwInputKeyProp(
+ VehicleHwKeyInputAction action, int32_t keyCode, int32_t targetDisplay) {
+ auto keyEvent = getValuePool()->obtain(VehiclePropertyType::INT32_VEC, 3);
+ keyEvent->prop = toInt(VehicleProperty::HW_KEY_INPUT);
+ keyEvent->areaId = 0;
+ keyEvent->timestamp = elapsedRealtimeNano();
+ keyEvent->status = VehiclePropertyStatus::AVAILABLE;
+ keyEvent->value.int32Values[0] = toInt(action);
+ keyEvent->value.int32Values[1] = keyCode;
+ keyEvent->value.int32Values[2] = targetDisplay;
+ return keyEvent;
+}
+
+StatusCode EmulatedVehicleServer::onSetProperty(const VehiclePropValue& value) {
+ // Some properties need to be treated non-trivially
+ switch (value.prop) {
+ case AP_POWER_STATE_REPORT:
+ switch (value.value.int32Values[0]) {
+ case toInt(VehicleApPowerStateReport::DEEP_SLEEP_EXIT):
+ case toInt(VehicleApPowerStateReport::SHUTDOWN_CANCELLED):
+ case toInt(VehicleApPowerStateReport::WAIT_FOR_VHAL):
+ // CPMS is in WAIT_FOR_VHAL state, simply move to ON
+ // Send back to HAL
+ onPropertyValueFromCar(
+ *createApPowerStateReq(VehicleApPowerStateReq::ON, 0));
+ break;
+ case toInt(VehicleApPowerStateReport::DEEP_SLEEP_ENTRY):
+ case toInt(VehicleApPowerStateReport::SHUTDOWN_START):
+ // CPMS is in WAIT_FOR_FINISH state, send the FINISHED command
+ // Send back to HAL
+ onPropertyValueFromCar(
+ *createApPowerStateReq(VehicleApPowerStateReq::FINISHED, 0));
+ break;
+ case toInt(VehicleApPowerStateReport::ON):
+ case toInt(VehicleApPowerStateReport::SHUTDOWN_POSTPONE):
+ case toInt(VehicleApPowerStateReport::SHUTDOWN_PREPARE):
+ // Do nothing
+ break;
+ default:
+ // Unknown state
+ break;
+ }
+ break;
+ default:
+ break;
+ }
+
+ // In the real vhal, the value will be sent to Car ECU.
+ // We just pretend it is done here.
+ return StatusCode::OK;
+}
+
+StatusCode EmulatedVehicleServer::onSetPropertyFromVehicle(const VehiclePropValue& value) {
+ if (value.prop == kGenerateFakeDataControllingProperty) {
+ auto status = handleGenerateFakeDataRequest(value);
+ return status;
+ } else {
+ // Send back to HAL
+ onPropertyValueFromCar(value);
+ return StatusCode::OK;
+ }
+}
+
+EmulatedPassthroughConnectorPtr makeEmulatedPassthroughConnector() {
+ return std::make_unique<EmulatedPassthroughConnector>();
+}
+
+} // namespace impl
+
+} // namespace V2_0
+} // namespace vehicle
+} // namespace automotive
+} // namespace hardware
+} // namespace android
diff --git a/automotive/vehicle/2.0/default/impl/vhal_v2_0/EmulatedVehicleConnector.h b/automotive/vehicle/2.0/default/impl/vhal_v2_0/EmulatedVehicleConnector.h
new file mode 100644
index 0000000..d424cd8
--- /dev/null
+++ b/automotive/vehicle/2.0/default/impl/vhal_v2_0/EmulatedVehicleConnector.h
@@ -0,0 +1,110 @@
+/*
+ * 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.
+ */
+
+#ifndef android_hardware_automotive_vehicle_V2_0_impl_EmulatedVehicleConnector_H_
+#define android_hardware_automotive_vehicle_V2_0_impl_EmulatedVehicleConnector_H_
+
+#include <vhal_v2_0/VehicleConnector.h>
+#include <vhal_v2_0/VehicleHal.h>
+
+#include "GeneratorHub.h"
+
+namespace android {
+namespace hardware {
+namespace automotive {
+namespace vehicle {
+namespace V2_0 {
+
+namespace impl {
+
+// Extension of the client/server interfaces for emulated vehicle
+
+class EmulatedVehicleClient : public IVehicleClient {
+ public:
+ // Type of callback function for handling the new property values
+ using PropertyCallBackType = std::function<void(const VehiclePropValue&)>;
+
+ // Method from IVehicleClient
+ void onPropertyValue(const VehiclePropValue& value) override;
+
+ // Request to change the value on the VEHICLE side (for testing)
+ virtual StatusCode setPropertyFromVehicle(const VehiclePropValue& value) = 0;
+
+ void registerPropertyValueCallback(PropertyCallBackType&& callback);
+
+ private:
+ PropertyCallBackType mPropCallback;
+};
+
+class EmulatedVehicleServer : public IVehicleServer {
+ public:
+ // Methods from IVehicleServer
+
+ std::vector<VehiclePropConfig> onGetAllPropertyConfig() const override;
+
+ StatusCode onSetProperty(const VehiclePropValue& value) override;
+
+ // Process the request to change the value on the VEHICLE side (for testing)
+ StatusCode onSetPropertyFromVehicle(const VehiclePropValue& value);
+
+ // Set the Property Value Pool used in this server
+ void setValuePool(VehiclePropValuePool* valuePool);
+
+ private:
+ GeneratorHub* getGenerator();
+
+ VehiclePropValuePool* getValuePool() const;
+
+ void onFakeValueGenerated(const VehiclePropValue& value);
+
+ StatusCode handleGenerateFakeDataRequest(const VehiclePropValue& request);
+
+ VehicleHal::VehiclePropValuePtr createApPowerStateReq(VehicleApPowerStateReq req, int32_t param);
+
+ VehicleHal::VehiclePropValuePtr createHwInputKeyProp(VehicleHwKeyInputAction action,
+ int32_t keyCode, int32_t targetDisplay);
+
+ // private data members
+
+ GeneratorHub mGeneratorHub{
+ std::bind(&EmulatedVehicleServer::onFakeValueGenerated, this, std::placeholders::_1)};
+
+ VehiclePropValuePool* mValuePool{nullptr};
+};
+
+class EmulatedPassthroughConnector
+ : public IPassThroughConnector<EmulatedVehicleClient, EmulatedVehicleServer> {
+ public:
+ StatusCode setPropertyFromVehicle(const VehiclePropValue& value) override {
+ return this->onSetPropertyFromVehicle(value);
+ }
+};
+
+// Helper functions
+
+using EmulatedPassthroughConnectorPtr = std::unique_ptr<EmulatedPassthroughConnector>;
+
+EmulatedPassthroughConnectorPtr makeEmulatedPassthroughConnector();
+
+} // namespace impl
+
+} // namespace V2_0
+} // namespace vehicle
+} // namespace automotive
+} // namespace hardware
+} // namespace android
+
+#endif // android_hardware_automotive_vehicle_V2_0_impl_EmulatedVehicleConnector_H_
diff --git a/automotive/vehicle/2.0/default/impl/vhal_v2_0/EmulatedVehicleHal.cpp b/automotive/vehicle/2.0/default/impl/vhal_v2_0/EmulatedVehicleHal.cpp
index dc051d8..6508efe 100644
--- a/automotive/vehicle/2.0/default/impl/vhal_v2_0/EmulatedVehicleHal.cpp
+++ b/automotive/vehicle/2.0/default/impl/vhal_v2_0/EmulatedVehicleHal.cpp
@@ -87,17 +87,19 @@
return sensorStore;
}
-EmulatedVehicleHal::EmulatedVehicleHal(VehiclePropertyStore* propStore)
+EmulatedVehicleHal::EmulatedVehicleHal(VehiclePropertyStore* propStore,
+ EmulatedVehicleClient* client)
: mPropStore(propStore),
mHvacPowerProps(std::begin(kHvacPowerProperties), std::end(kHvacPowerProperties)),
mRecurrentTimer(
std::bind(&EmulatedVehicleHal::onContinuousPropertyTimer, this, std::placeholders::_1)),
- mGeneratorHub(
- std::bind(&EmulatedVehicleHal::onFakeValueGenerated, this, std::placeholders::_1)) {
+ mVehicleClient(client) {
initStaticConfig();
for (size_t i = 0; i < arraysize(kVehicleProperties); i++) {
mPropStore->registerProperty(kVehicleProperties[i].config);
}
+ mVehicleClient->registerPropertyValueCallback(
+ std::bind(&EmulatedVehicleHal::onPropertyValue, this, std::placeholders::_1));
}
VehicleHal::VehiclePropValuePtr EmulatedVehicleHal::get(
@@ -162,7 +164,8 @@
}
if (propValue.prop == kGenerateFakeDataControllingProperty) {
- StatusCode status = handleGenerateFakeDataRequest(propValue);
+ // send the generator controlling request to the server
+ auto status = mVehicleClient->setPropertyFromVehicle(propValue);
if (status != StatusCode::OK) {
return status;
}
@@ -186,29 +189,6 @@
// Placeholder for future implementation of VMS property in the default hal. For
// now, just returns OK; otherwise, hal clients crash with property not supported.
return StatusCode::OK;
- case AP_POWER_STATE_REPORT:
- switch (propValue.value.int32Values[0]) {
- case toInt(VehicleApPowerStateReport::DEEP_SLEEP_EXIT):
- case toInt(VehicleApPowerStateReport::SHUTDOWN_CANCELLED):
- case toInt(VehicleApPowerStateReport::WAIT_FOR_VHAL):
- // CPMS is in WAIT_FOR_VHAL state, simply move to ON
- doHalEvent(createApPowerStateReq(VehicleApPowerStateReq::ON, 0));
- break;
- case toInt(VehicleApPowerStateReport::DEEP_SLEEP_ENTRY):
- case toInt(VehicleApPowerStateReport::SHUTDOWN_START):
- // CPMS is in WAIT_FOR_FINISH state, send the FINISHED command
- doHalEvent(createApPowerStateReq(VehicleApPowerStateReq::FINISHED, 0));
- break;
- case toInt(VehicleApPowerStateReport::ON):
- case toInt(VehicleApPowerStateReport::SHUTDOWN_POSTPONE):
- case toInt(VehicleApPowerStateReport::SHUTDOWN_PREPARE):
- // Do nothing
- break;
- default:
- // Unknown state
- break;
- }
- break;
}
}
@@ -231,10 +211,16 @@
/**
* After checking all conditions, such as the property is available, a real vhal will
* sent the events to Car ECU to take actions.
- * Google HAL will just add a timestamp for the value and triggle the callback to android.
*/
VehiclePropValuePtr updatedPropValue = getValuePool()->obtain(propValue);
updatedPropValue->timestamp = elapsedRealtimeNano();
+
+ // Send the value to the vehicle server, the server will talk to the (real or emulated) car
+ auto setValueStatus = mVehicleClient->setProperty(*updatedPropValue);
+ if (setValueStatus != StatusCode::OK) {
+ return setValueStatus;
+ }
+
if (!mPropStore->writeValue(*updatedPropValue, shouldUpdateStatus)) {
return StatusCode::INTERNAL_ERROR;
}
@@ -361,144 +347,19 @@
}
bool EmulatedVehicleHal::setPropertyFromVehicle(const VehiclePropValue& propValue) {
- static constexpr bool shouldUpdateStatus = true;
-
- if (propValue.prop == kGenerateFakeDataControllingProperty) {
- StatusCode status = handleGenerateFakeDataRequest(propValue);
- if (status != StatusCode::OK) {
- return false;
- }
- }
-
- if (mPropStore->writeValue(propValue, shouldUpdateStatus)) {
- doHalEvent(getValuePool()->obtain(propValue));
- return true;
- } else {
- return false;
- }
+ return mVehicleClient->setPropertyFromVehicle(propValue) == StatusCode::OK;
}
std::vector<VehiclePropValue> EmulatedVehicleHal::getAllProperties() const {
return mPropStore->readAllValues();
}
-StatusCode EmulatedVehicleHal::handleGenerateFakeDataRequest(const VehiclePropValue& request) {
- ALOGI("%s", __func__);
- const auto& v = request.value;
- if (!v.int32Values.size()) {
- ALOGE("%s: expected at least \"command\" field in int32Values", __func__);
- return StatusCode::INVALID_ARG;
- }
-
- FakeDataCommand command = static_cast<FakeDataCommand>(v.int32Values[0]);
-
- switch (command) {
- case FakeDataCommand::StartLinear: {
- ALOGI("%s, FakeDataCommand::StartLinear", __func__);
- if (v.int32Values.size() < 2) {
- ALOGE("%s: expected property ID in int32Values", __func__);
- return StatusCode::INVALID_ARG;
- }
- if (!v.int64Values.size()) {
- ALOGE("%s: interval is not provided in int64Values", __func__);
- return StatusCode::INVALID_ARG;
- }
- if (v.floatValues.size() < 3) {
- ALOGE("%s: expected at least 3 elements in floatValues, got: %zu", __func__,
- v.floatValues.size());
- return StatusCode::INVALID_ARG;
- }
- int32_t cookie = v.int32Values[1];
- mGeneratorHub.registerGenerator(cookie,
- std::make_unique<LinearFakeValueGenerator>(request));
- break;
- }
- case FakeDataCommand::StartJson: {
- ALOGI("%s, FakeDataCommand::StartJson", __func__);
- if (v.stringValue.empty()) {
- ALOGE("%s: path to JSON file is missing", __func__);
- return StatusCode::INVALID_ARG;
- }
- int32_t cookie = std::hash<std::string>()(v.stringValue);
- mGeneratorHub.registerGenerator(cookie,
- std::make_unique<JsonFakeValueGenerator>(request));
- break;
- }
- case FakeDataCommand::StopLinear: {
- ALOGI("%s, FakeDataCommand::StopLinear", __func__);
- if (v.int32Values.size() < 2) {
- ALOGE("%s: expected property ID in int32Values", __func__);
- return StatusCode::INVALID_ARG;
- }
- int32_t cookie = v.int32Values[1];
- mGeneratorHub.unregisterGenerator(cookie);
- break;
- }
- case FakeDataCommand::StopJson: {
- ALOGI("%s, FakeDataCommand::StopJson", __func__);
- if (v.stringValue.empty()) {
- ALOGE("%s: path to JSON file is missing", __func__);
- return StatusCode::INVALID_ARG;
- }
- int32_t cookie = std::hash<std::string>()(v.stringValue);
- mGeneratorHub.unregisterGenerator(cookie);
- break;
- }
- case FakeDataCommand::KeyPress: {
- ALOGI("%s, FakeDataCommand::KeyPress", __func__);
- int32_t keyCode = request.value.int32Values[2];
- int32_t display = request.value.int32Values[3];
- doHalEvent(
- createHwInputKeyProp(VehicleHwKeyInputAction::ACTION_DOWN, keyCode, display));
- doHalEvent(createHwInputKeyProp(VehicleHwKeyInputAction::ACTION_UP, keyCode, display));
- break;
- }
- default: {
- ALOGE("%s: unexpected command: %d", __func__, command);
- return StatusCode::INVALID_ARG;
- }
- }
- return StatusCode::OK;
-}
-
-VehicleHal::VehiclePropValuePtr EmulatedVehicleHal::createApPowerStateReq(
- VehicleApPowerStateReq state, int32_t param) {
- auto req = getValuePool()->obtain(VehiclePropertyType::INT32_VEC, 2);
- req->prop = toInt(VehicleProperty::AP_POWER_STATE_REQ);
- req->areaId = 0;
- req->timestamp = elapsedRealtimeNano();
- req->status = VehiclePropertyStatus::AVAILABLE;
- req->value.int32Values[0] = toInt(state);
- req->value.int32Values[1] = param;
- return req;
-}
-
-VehicleHal::VehiclePropValuePtr EmulatedVehicleHal::createHwInputKeyProp(
- VehicleHwKeyInputAction action, int32_t keyCode, int32_t targetDisplay) {
- auto keyEvent = getValuePool()->obtain(VehiclePropertyType::INT32_VEC, 3);
- keyEvent->prop = toInt(VehicleProperty::HW_KEY_INPUT);
- keyEvent->areaId = 0;
- keyEvent->timestamp = elapsedRealtimeNano();
- keyEvent->status = VehiclePropertyStatus::AVAILABLE;
- keyEvent->value.int32Values[0] = toInt(action);
- keyEvent->value.int32Values[1] = keyCode;
- keyEvent->value.int32Values[2] = targetDisplay;
- return keyEvent;
-}
-
-void EmulatedVehicleHal::onFakeValueGenerated(const VehiclePropValue& value) {
- ALOGD("%s: %s", __func__, toString(value).c_str());
- static constexpr bool shouldUpdateStatus = false;
-
+void EmulatedVehicleHal::onPropertyValue(const VehiclePropValue& value) {
+ static constexpr bool shouldUpdateStatus = true;
VehiclePropValuePtr updatedPropValue = getValuePool()->obtain(value);
- if (updatedPropValue) {
- updatedPropValue->timestamp = value.timestamp;
- updatedPropValue->status = VehiclePropertyStatus::AVAILABLE;
- mPropStore->writeValue(*updatedPropValue, shouldUpdateStatus);
- auto changeMode = mPropStore->getConfigOrDie(value.prop)->changeMode;
- if (VehiclePropertyChangeMode::ON_CHANGE == changeMode) {
- doHalEvent(std::move(updatedPropValue));
- }
+
+ if (mPropStore->writeValue(*updatedPropValue, shouldUpdateStatus)) {
+ doHalEvent(std::move(updatedPropValue));
}
}
diff --git a/automotive/vehicle/2.0/default/impl/vhal_v2_0/EmulatedVehicleHal.h b/automotive/vehicle/2.0/default/impl/vhal_v2_0/EmulatedVehicleHal.h
index 78895e3..98315ec 100644
--- a/automotive/vehicle/2.0/default/impl/vhal_v2_0/EmulatedVehicleHal.h
+++ b/automotive/vehicle/2.0/default/impl/vhal_v2_0/EmulatedVehicleHal.h
@@ -30,6 +30,7 @@
#include "vhal_v2_0/VehiclePropertyStore.h"
#include "DefaultConfig.h"
+#include "EmulatedVehicleConnector.h"
#include "GeneratorHub.h"
#include "VehicleEmulator.h"
@@ -44,7 +45,8 @@
/** Implementation of VehicleHal that connected to emulator instead of real vehicle network. */
class EmulatedVehicleHal : public EmulatedVehicleHalIface {
public:
- EmulatedVehicleHal(VehiclePropertyStore* propStore);
+ EmulatedVehicleHal(VehiclePropertyStore* propStore,
+ EmulatedVehicleClient* client);
~EmulatedVehicleHal() = default;
// Methods from VehicleHal
@@ -66,10 +68,7 @@
}
StatusCode handleGenerateFakeDataRequest(const VehiclePropValue& request);
- void onFakeValueGenerated(const VehiclePropValue& value);
- VehiclePropValuePtr createApPowerStateReq(VehicleApPowerStateReq req, int32_t param);
- VehiclePropValuePtr createHwInputKeyProp(VehicleHwKeyInputAction action, int32_t keyCode,
- int32_t targetDisplay);
+ void onPropertyValue(const VehiclePropValue& value);
void onContinuousPropertyTimer(const std::vector<int32_t>& properties);
bool isContinuousProperty(int32_t propId) const;
@@ -85,7 +84,7 @@
VehiclePropertyStore* mPropStore;
std::unordered_set<int32_t> mHvacPowerProps;
RecurrentTimer mRecurrentTimer;
- GeneratorHub mGeneratorHub;
+ EmulatedVehicleClient* mVehicleClient;
};
} // impl
diff --git a/automotive/vehicle/2.0/default/impl/vhal_v2_0/proto/Android.bp b/automotive/vehicle/2.0/default/impl/vhal_v2_0/proto/Android.bp
index 6754843..2eedecd 100644
--- a/automotive/vehicle/2.0/default/impl/vhal_v2_0/proto/Android.bp
+++ b/automotive/vehicle/2.0/default/impl/vhal_v2_0/proto/Android.bp
@@ -29,3 +29,64 @@
],
srcs: ["VehicleHalProto.proto"]
}
+
+cc_library_static {
+ name: "android.hardware.automotive.vehicle@2.0-grpc",
+ vendor: true,
+ include_dirs: [
+ "external/protobuf/src",
+ ],
+ generated_headers: [
+ "DefaultVehicleHalProtoStub_h",
+ ],
+ export_generated_headers: [
+ "DefaultVehicleHalProtoStub_h",
+ ],
+ generated_sources: [
+ "DefaultVehicleHalProtoStub_cc",
+ ],
+ shared_libs: [
+ "libgrpc++_unsecure",
+ ],
+ cflags: [
+ "-Wno-unused-parameter"
+ ],
+}
+
+genrule {
+ name: "DefaultVehicleHalProtoStub_h",
+ tools: [
+ "aprotoc",
+ "protoc-gen-grpc-cpp-plugin",
+ ],
+ cmd: "$(location aprotoc) -I$$(dirname $(in)) -Iexternal/protobuf/src --plugin=protoc-gen-grpc=$(location protoc-gen-grpc-cpp-plugin) $(in) --grpc_out=$(genDir) --cpp_out=$(genDir)",
+ srcs: [
+ "VehicleHalProto.proto",
+ "VehicleServer.proto",
+ ],
+ out: [
+ "VehicleHalProto.pb.h",
+ "VehicleHalProto.grpc.pb.h",
+ "VehicleServer.pb.h",
+ "VehicleServer.grpc.pb.h",
+ ],
+}
+
+genrule {
+ name: "DefaultVehicleHalProtoStub_cc",
+ tools: [
+ "aprotoc",
+ "protoc-gen-grpc-cpp-plugin",
+ ],
+ cmd: "$(location aprotoc) -I$$(dirname $(in)) -Iexternal/protobuf/src --plugin=protoc-gen-grpc=$(location protoc-gen-grpc-cpp-plugin) $(in) --grpc_out=$(genDir) --cpp_out=$(genDir)",
+ srcs: [
+ "VehicleHalProto.proto",
+ "VehicleServer.proto",
+ ],
+ out: [
+ "VehicleHalProto.pb.cc",
+ "VehicleHalProto.grpc.pb.cc",
+ "VehicleServer.pb.cc",
+ "VehicleServer.grpc.pb.cc",
+ ],
+}
diff --git a/automotive/vehicle/2.0/default/impl/vhal_v2_0/proto/VehicleHalProto.proto b/automotive/vehicle/2.0/default/impl/vhal_v2_0/proto/VehicleHalProto.proto
index 2ef64fb..04df5a8 100644
--- a/automotive/vehicle/2.0/default/impl/vhal_v2_0/proto/VehicleHalProto.proto
+++ b/automotive/vehicle/2.0/default/impl/vhal_v2_0/proto/VehicleHalProto.proto
@@ -15,7 +15,6 @@
*/
syntax = "proto2";
-option optimize_for = LITE_RUNTIME;
package emulator;
diff --git a/automotive/vehicle/2.0/default/impl/vhal_v2_0/proto/VehicleServer.proto b/automotive/vehicle/2.0/default/impl/vhal_v2_0/proto/VehicleServer.proto
new file mode 100644
index 0000000..7ce3c32
--- /dev/null
+++ b/automotive/vehicle/2.0/default/impl/vhal_v2_0/proto/VehicleServer.proto
@@ -0,0 +1,47 @@
+/*
+ * 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.
+ */
+
+syntax = "proto2";
+
+package emulator;
+
+import "google/protobuf/empty.proto";
+import "VehicleHalProto.proto";
+
+// correspond to StatusCode defined in types.hal
+enum VehicleHalStatusCode {
+ OK = 0;
+ TRY_AGAIN = 1;
+ INVALID_ARG = 2;
+ NOT_AVAILABLE = 3;
+ ACCESS_DENIED = 4;
+ INTERNAL_ERROR = 5;
+}
+
+message VehicleHalCallStatus {
+ required VehicleHalStatusCode status_code = 1;
+}
+
+service VehicleServer {
+ rpc GetAllPropertyConfig(google.protobuf.Empty) returns (stream VehiclePropConfig) {}
+
+ // Change the property value of the vehicle
+ rpc SetProperty(VehiclePropValue) returns (VehicleHalCallStatus) {}
+
+ // Start a vehicle property value stream
+ rpc StartPropertyValuesStream(google.protobuf.Empty) returns (stream VehiclePropValue) {}
+}
+
diff --git a/biometrics/face/1.0/vts/functional/Android.bp b/biometrics/face/1.0/vts/functional/Android.bp
index fa68c4e..f2598a7 100644
--- a/biometrics/face/1.0/vts/functional/Android.bp
+++ b/biometrics/face/1.0/vts/functional/Android.bp
@@ -19,6 +19,6 @@
defaults: ["VtsHalTargetTestDefaults"],
srcs: ["VtsHalBiometricsFaceV1_0TargetTest.cpp"],
static_libs: ["android.hardware.biometrics.face@1.0"],
- test_suites: ["general-tests"],
+ test_suites: ["general-tests", "vts-core"],
}
diff --git a/biometrics/face/1.0/vts/functional/VtsHalBiometricsFaceV1_0TargetTest.cpp b/biometrics/face/1.0/vts/functional/VtsHalBiometricsFaceV1_0TargetTest.cpp
index a4e95ed..7ac44a4 100644
--- a/biometrics/face/1.0/vts/functional/VtsHalBiometricsFaceV1_0TargetTest.cpp
+++ b/biometrics/face/1.0/vts/functional/VtsHalBiometricsFaceV1_0TargetTest.cpp
@@ -20,9 +20,10 @@
#include <android/hardware/biometrics/face/1.0/IBiometricsFaceClientCallback.h>
#include <VtsHalHidlTargetCallbackBase.h>
-#include <VtsHalHidlTargetTestBase.h>
-#include <VtsHalHidlTargetTestEnvBase.h>
#include <android-base/logging.h>
+#include <gtest/gtest.h>
+#include <hidl/GtestPrinter.h>
+#include <hidl/ServiceManagement.h>
#include <chrono>
#include <cstdint>
@@ -124,27 +125,11 @@
}
};
-// Test environment for the BiometricsFace HAL.
-class FaceHidlEnvironment : public ::testing::VtsHalHidlTargetTestEnvBase {
- public:
- // Get the test environment singleton.
- static FaceHidlEnvironment* Instance() {
- static FaceHidlEnvironment* instance = new FaceHidlEnvironment;
- return instance;
- }
-
- void registerTestServices() override { registerTestService<IBiometricsFace>(); }
-
- private:
- FaceHidlEnvironment() = default;
-};
-
// Test class for the BiometricsFace HAL.
-class FaceHidlTest : public ::testing::VtsHalHidlTargetTestBase {
+class FaceHidlTest : public ::testing::TestWithParam<std::string> {
public:
void SetUp() override {
- mService = ::testing::VtsHalHidlTargetTestBase::getService<IBiometricsFace>(
- FaceHidlEnvironment::Instance()->getServiceName<IBiometricsFace>());
+ mService = IBiometricsFace::getService(GetParam());
ASSERT_NE(mService, nullptr);
mCallback = new FaceCallback();
mCallback->SetWaitTimeoutDefault(kTimeout);
@@ -167,7 +152,7 @@
// generateChallenge should always return a unique, cryptographically secure,
// non-zero number.
-TEST_F(FaceHidlTest, GenerateChallengeTest) {
+TEST_P(FaceHidlTest, GenerateChallengeTest) {
std::map<uint64_t, int> m;
for (int i = 0; i < kGenerateChallengeIterations; ++i) {
Return<void> ret =
@@ -182,7 +167,7 @@
}
// enroll with an invalid (all zeroes) HAT should fail.
-TEST_F(FaceHidlTest, EnrollZeroHatTest) {
+TEST_P(FaceHidlTest, EnrollZeroHatTest) {
// Filling HAT with zeros
hidl_vec<uint8_t> token(69);
for (size_t i = 0; i < 69; i++) {
@@ -200,7 +185,7 @@
}
// enroll with an invalid HAT should fail.
-TEST_F(FaceHidlTest, EnrollGarbageHatTest) {
+TEST_P(FaceHidlTest, EnrollGarbageHatTest) {
// Filling HAT with pseudorandom invalid data.
// Using default seed to make the test reproducible.
std::mt19937 gen(std::mt19937::default_seed);
@@ -221,7 +206,7 @@
}
// setFeature with an invalid (all zeros) HAT should fail.
-TEST_F(FaceHidlTest, SetFeatureZeroHatTest) {
+TEST_P(FaceHidlTest, SetFeatureZeroHatTest) {
hidl_vec<uint8_t> token(69);
for (size_t i = 0; i < 69; i++) {
token[i] = 0;
@@ -232,7 +217,7 @@
}
// setFeature with an invalid HAT should fail.
-TEST_F(FaceHidlTest, SetFeatureGarbageHatTest) {
+TEST_P(FaceHidlTest, SetFeatureGarbageHatTest) {
// Filling HAT with pseudorandom invalid data.
// Using default seed to make the test reproducible.
std::mt19937 gen(std::mt19937::default_seed);
@@ -254,16 +239,16 @@
ASSERT_TRUE(res.isOk());
}
-TEST_F(FaceHidlTest, GetFeatureRequireAttentionTest) {
+TEST_P(FaceHidlTest, GetFeatureRequireAttentionTest) {
assertGetFeatureFails(mService, 0 /* faceId */, Feature::REQUIRE_ATTENTION);
}
-TEST_F(FaceHidlTest, GetFeatureRequireDiversityTest) {
+TEST_P(FaceHidlTest, GetFeatureRequireDiversityTest) {
assertGetFeatureFails(mService, 0 /* faceId */, Feature::REQUIRE_DIVERSITY);
}
// revokeChallenge should always return within the timeout
-TEST_F(FaceHidlTest, RevokeChallengeTest) {
+TEST_P(FaceHidlTest, RevokeChallengeTest) {
auto start = std::chrono::system_clock::now();
Return<Status> ret = mService->revokeChallenge();
auto elapsed = std::chrono::system_clock::now() - start;
@@ -272,14 +257,14 @@
}
// The call to getAuthenticatorId should succeed.
-TEST_F(FaceHidlTest, GetAuthenticatorIdTest) {
+TEST_P(FaceHidlTest, GetAuthenticatorIdTest) {
Return<void> ret = mService->getAuthenticatorId(
[](const OptionalUint64& res) { ASSERT_EQ(Status::OK, res.status); });
ASSERT_TRUE(ret.isOk());
}
// The call to enumerate should succeed.
-TEST_F(FaceHidlTest, EnumerateTest) {
+TEST_P(FaceHidlTest, EnumerateTest) {
Return<Status> ret = mService->enumerate();
ASSERT_EQ(Status::OK, static_cast<Status>(ret));
auto res = mCallback->WaitForCallback(kCallbackNameOnEnumerate);
@@ -288,21 +273,21 @@
}
// The call to remove should succeed for any faceId
-TEST_F(FaceHidlTest, RemoveFaceTest) {
+TEST_P(FaceHidlTest, RemoveFaceTest) {
// Remove a face
Return<Status> ret = mService->remove(kFaceId);
ASSERT_EQ(Status::OK, static_cast<Status>(ret));
}
// Remove should accept 0 to delete all faces
-TEST_F(FaceHidlTest, RemoveAllFacesTest) {
+TEST_P(FaceHidlTest, RemoveAllFacesTest) {
// Remove all faces
Return<Status> ret = mService->remove(0);
ASSERT_EQ(Status::OK, static_cast<Status>(ret));
}
// Active user should successfully set to a writable location.
-TEST_F(FaceHidlTest, SetActiveUserTest) {
+TEST_P(FaceHidlTest, SetActiveUserTest) {
// Create an active user
Return<Status> ret = mService->setActiveUser(2, kFacedataDir);
ASSERT_EQ(Status::OK, static_cast<Status>(ret));
@@ -313,7 +298,7 @@
}
// Active user should fail to set to an unwritable location.
-TEST_F(FaceHidlTest, SetActiveUserUnwritableTest) {
+TEST_P(FaceHidlTest, SetActiveUserUnwritableTest) {
// Create an active user to an unwritable location (device root dir)
Return<Status> ret = mService->setActiveUser(3, "/");
ASSERT_NE(Status::OK, static_cast<Status>(ret));
@@ -324,7 +309,7 @@
}
// Active user should fail to set to a null location.
-TEST_F(FaceHidlTest, SetActiveUserNullTest) {
+TEST_P(FaceHidlTest, SetActiveUserNullTest) {
// Create an active user to a null location.
Return<Status> ret = mService->setActiveUser(4, nullptr);
ASSERT_NE(Status::OK, static_cast<Status>(ret));
@@ -336,7 +321,7 @@
// Cancel should always return CANCELED from any starting state including
// the IDLE state.
-TEST_F(FaceHidlTest, CancelTest) {
+TEST_P(FaceHidlTest, CancelTest) {
Return<Status> ret = mService->cancel();
// check that we were able to make an IPC request successfully
ASSERT_EQ(Status::OK, static_cast<Status>(ret));
@@ -347,7 +332,7 @@
EXPECT_EQ(FaceError::CANCELED, res.args->error);
}
-TEST_F(FaceHidlTest, OnLockoutChangedTest) {
+TEST_P(FaceHidlTest, OnLockoutChangedTest) {
// Update active user and ensure onLockoutChanged was called.
Return<Status> ret = mService->setActiveUser(kUserId + 1, kFacedataDir);
ASSERT_EQ(Status::OK, static_cast<Status>(ret));
@@ -359,11 +344,7 @@
} // anonymous namespace
-int main(int argc, char** argv) {
- ::testing::AddGlobalTestEnvironment(FaceHidlEnvironment::Instance());
- ::testing::InitGoogleTest(&argc, argv);
- FaceHidlEnvironment::Instance()->init(&argc, argv);
- int status = RUN_ALL_TESTS();
- LOG(INFO) << "Test result = " << status;
- return status;
-}
+INSTANTIATE_TEST_SUITE_P(
+ PerInstance, FaceHidlTest,
+ testing::ValuesIn(android::hardware::getAllHalInstanceNames(IBiometricsFace::descriptor)),
+ android::hardware::PrintInstanceNameToString);
diff --git a/camera/common/1.0/default/HandleImporter.cpp b/camera/common/1.0/default/HandleImporter.cpp
index 76f9778..ac32c95 100644
--- a/camera/common/1.0/default/HandleImporter.cpp
+++ b/camera/common/1.0/default/HandleImporter.cpp
@@ -252,8 +252,7 @@
// No need to use bytesPerPixel and bytesPerStride because we are using
// an 1-D buffer and accressRegion.
mMapperV4->lock(buffer, cpuUsage, accessRegion, acquireFenceHandle,
- [&](const auto& tmpError, const auto& tmpPtr, const auto& /*bytesPerPixel*/,
- const auto& /*bytesPerStride*/) {
+ [&](const auto& tmpError, const auto& tmpPtr) {
if (tmpError == MapperErrorV4::NONE) {
ret = tmpPtr;
} else {
@@ -301,7 +300,13 @@
}
if (mMapperV4 != nullptr) {
- return lockYCbCrInternal<IMapperV4, MapperErrorV4>(mMapperV4, buf, cpuUsage, accessRegion);
+ // No device currently supports IMapper 4.0 so it is safe to just return an error code here.
+ //
+ // This will be supported by a combination of lock and BufferMetadata getters. We are going
+ // to refactor all the IAllocator/IMapper versioning code into a shared library. We will
+ // then add the IMapper 4.0 lockYCbCr support then.
+ ALOGE("%s: MapperV4 doesn't support lockYCbCr directly!", __FUNCTION__);
+ return {};
}
if (mMapperV3 != nullptr) {
diff --git a/compatibility_matrices/compatibility_matrix.current.xml b/compatibility_matrices/compatibility_matrix.current.xml
index 7d4f7a3..4bd8332 100644
--- a/compatibility_matrices/compatibility_matrix.current.xml
+++ b/compatibility_matrices/compatibility_matrix.current.xml
@@ -256,7 +256,7 @@
<hal format="hidl" optional="false">
<name>android.hardware.keymaster</name>
<version>3.0</version>
- <version>4.0</version>
+ <version>4.0-1</version>
<interface>
<name>IKeymasterDevice</name>
<instance>default</instance>
@@ -264,7 +264,7 @@
</hal>
<hal format="hidl" optional="true">
<name>android.hardware.keymaster</name>
- <version>4.0</version>
+ <version>4.0-1</version>
<interface>
<name>IKeymasterDevice</name>
<instance>strongbox</instance>
diff --git a/current.txt b/current.txt
index 68d7d73..d33d1b7 100644
--- a/current.txt
+++ b/current.txt
@@ -604,7 +604,7 @@
9e59fffceed0dd72a9799e04505db5f777bbbea1af0695ba4107ef6d967c6fda android.hardware.neuralnetworks@1.3::IDevice
4a6c3b3556da951b4def21ba579a227c022980fe4465df6cdfbe20628fa75f5a android.hardware.neuralnetworks@1.3::IPreparedModel
94e803236398bed1febb11cc21051bc42ec003700139b099d6c479e02a7ca3c3 android.hardware.neuralnetworks@1.3::IPreparedModelCallback
-554baa3b317e077b850afcbaac99daeef56861b1786540e56275a4fcad1f43e3 android.hardware.neuralnetworks@1.3::types
+2d16429145dc1158bf3e45c7de86a39e461dec3ec00512c11a7e5249535a2e96 android.hardware.neuralnetworks@1.3::types
3e01d4446cd69fd1c48f8572efd97487bc179564b32bd795800b97bbe10be37b android.hardware.wifi@1.4::IWifi
a64467bae843569f0d465c5be7f0c7a5b987985b55a3ef4794dd5afc68538650 android.hardware.wifi.supplicant@1.3::ISupplicant
44445b8a03d7b9e68b2fbd954672c18a8fce9e32851b0692f4f4ab3407f86ecb android.hardware.wifi.supplicant@1.3::ISupplicantStaIface
diff --git a/graphics/common/aidl/Android.bp b/graphics/common/aidl/Android.bp
index 497c0f8..e0c7674 100644
--- a/graphics/common/aidl/Android.bp
+++ b/graphics/common/aidl/Android.bp
@@ -1,5 +1,6 @@
aidl_interface {
name: "vintf-graphics-common",
+ host_supported: true,
vendor_available: true,
vndk: {
enabled: true,
diff --git a/graphics/common/aidl/android/hardware/graphics/common/ExtendableType.aidl b/graphics/common/aidl/android/hardware/graphics/common/ExtendableType.aidl
index 04d8710..495693c 100644
--- a/graphics/common/aidl/android/hardware/graphics/common/ExtendableType.aidl
+++ b/graphics/common/aidl/android/hardware/graphics/common/ExtendableType.aidl
@@ -34,7 +34,7 @@
* For custom vendor types, the "name" field will be set to the name of the custom
* @VendorStability vendor AIDL interface such as
* "vendor.mycompanyname.graphics.common.Compression". The name of the vendor extension should
- * contain the name of the company that owns the extension. Including the company
+ * contain the name of the owner of the extension. Including the company
* name in the "name" field prevents type collisions between different vendors.
*/
@utf8InCpp String name;
diff --git a/graphics/composer/2.1/utils/vts/ComposerVts.cpp b/graphics/composer/2.1/utils/vts/ComposerVts.cpp
index a0745ce..a8e1480 100644
--- a/graphics/composer/2.1/utils/vts/ComposerVts.cpp
+++ b/graphics/composer/2.1/utils/vts/ComposerVts.cpp
@@ -369,10 +369,7 @@
accessRegion.top = accessRegionRect.top;
accessRegion.width = accessRegionRect.width;
accessRegion.height = accessRegionRect.height;
- int32_t bytesPerPixel;
- int32_t bytesPerStride;
- return mGralloc4->lock(bufferHandle, cpuUsage, accessRegion, acquireFence, &bytesPerPixel,
- &bytesPerStride);
+ return mGralloc4->lock(bufferHandle, cpuUsage, accessRegion, acquireFence);
} else if (mGralloc3) {
IMapper3::Rect accessRegion;
accessRegion.left = accessRegionRect.left;
diff --git a/graphics/composer/2.4/IComposerCallback.hal b/graphics/composer/2.4/IComposerCallback.hal
index 5c3f8bd..fea24a1 100644
--- a/graphics/composer/2.4/IComposerCallback.hal
+++ b/graphics/composer/2.4/IComposerCallback.hal
@@ -32,4 +32,14 @@
* is expected to be called vsyncPeriodNanos nanoseconds after this call.
*/
oneway onVsync_2_4(Display display, int64_t timestamp, VsyncPeriodNanos vsyncPeriodNanos);
+
+ /**
+ * Notifies the client that the previously reported timing for vsync period change has been
+ * updated. This may occur if the composer missed the deadline for changing the vsync period
+ * or the client submitted a refresh frame too late.
+ *
+ * @param display is the display which vsync period change is in progress
+ * @param updatedTimeline is the new timeline for the vsync period change.
+ */
+ oneway onVsyncPeriodTimingChanged(Display display, VsyncPeriodChangeTimeline updatedTimeline);
};
diff --git a/graphics/composer/2.4/IComposerClient.hal b/graphics/composer/2.4/IComposerClient.hal
index c2102d5..f23536c 100644
--- a/graphics/composer/2.4/IComposerClient.hal
+++ b/graphics/composer/2.4/IComposerClient.hal
@@ -20,10 +20,23 @@
import @2.1::Config;
import @2.1::Display;
import @2.1::Error;
+import @2.1::IComposerClient;
import @2.3::IComposerClient;
interface IComposerClient extends @2.3::IComposerClient {
/**
+ * Display attributes queryable through getDisplayAttribute_2_4.
+ */
+ enum Attribute : @2.1::IComposerClient.Attribute {
+ /**
+ * The configuration group ID (as int32_t) this config is associated to.
+ * Switching between configurations within the same group may be done seamlessly
+ * in some conditions via setActiveConfigWithConstraints.
+ */
+ CONFIG_GROUP = 7,
+ };
+
+ /**
* Required capabilities which are supported by the display. The
* particular set of supported capabilities for a given display may be
* retrieved using getDisplayCapabilities.
@@ -60,6 +73,7 @@
* (i.e., the vsync period must not change before this time).
*/
int64_t desiredTimeNanos;
+
/**
* If true, requires that the vsync period change must happen seamlessly without
* a noticeable visual artifact.
@@ -74,7 +88,6 @@
*
* @param callback is the IComposerCallback object.
*/
- @entry
registerCallback_2_4(IComposerCallback callback);
/**
@@ -99,25 +112,29 @@
getDisplayConnectionType(Display display) generates (Error error, DisplayConnectionType type);
/**
- * Provides a list of the vsync periods supported by the display in the given configuration
+ * Returns a display attribute value for a particular display
+ * configuration.
*
- * @param display is the display for which the vsync periods are queried.
- * @param config is the display configuration for which the vsync periods are queried.
- *
+ * @param display is the display to query.
+ * @param config is the display configuration for which to return
+ * attribute values.
* @return error is NONE upon success. Otherwise,
- * BAD_DISPLAY when an invalid display handle was passed in.
- * BAD_CONFIG when an invalid config handle was passed in.
- * @return supportedVsyncPeriods is a list of supported vsync periods.
+ * BAD_DISPLAY when an invalid display handle was passed in.
+ * BAD_CONFIG when config does not name a valid configuration for
+ * this display.
+ * BAD_PARAMETER when attribute is unrecognized.
+ * UNSUPPORTED when attribute cannot be queried for the config.
+ * @return value is the value of the attribute.
*/
- getSupportedDisplayVsyncPeriods(Display display, Config config)
- generates (Error error, vec<VsyncPeriodNanos> supportedVsyncPeriods);
+ getDisplayAttribute_2_4(Display display, Config config, Attribute attribute)
+ generates (Error error, int32_t value);
/**
* Retrieves which vsync period the display is currently using.
*
* If no display configuration is currently active, this function must
* return BAD_CONFIG. If the vsync period is about to change due to a
- * setActiveConfigAndVsyncPeriod call, this function must return the current vsync period
+ * setActiveConfigWithConstraints call, this function must return the current vsync period
* until the change takes place.
*
* @param display is the display for which the vsync period is queried.
@@ -131,7 +148,8 @@
/**
* Sets the active configuration and the refresh rate for this display.
- * If the config is the same as the current config, only the vsync period shall change.
+ * If the new config shares the same config group as the current config,
+ * only the vsync period shall change.
* Upon returning, the given display configuration, except vsync period, must be active and
* remain so until either this function is called again or the display is disconnected.
* When the display starts to refresh at the new vsync period, onVsync_2_4 callback must be
@@ -139,21 +157,19 @@
*
* @param display is the display for which the active config is set.
* @param config is the new display configuration.
- * @param vsyncPeriodNanos is the new display vsync period.
* @param vsyncPeriodChangeConstraints are the constraints required for changing vsync period.
*
* @return error is NONE upon success. Otherwise,
* BAD_DISPLAY when an invalid display handle was passed in.
* BAD_CONFIG when the configuration handle passed in is not valid
* for this display.
- * BAD_VSYNC_PERIOD when an invalid vsync period is passed in.
+ * SEAMLESS_NOT_ALLOWED when seamlessRequired was true but config provided doesn't
+ * share the same config group as the current config.
* SEAMLESS_NOT_POSSIBLE when seamlessRequired was true but the display cannot achieve
* the vsync period change without a noticeable visual artifact.
- * @return newVsyncAppliedTime is the time in CLOCK_MONOTONIC when the new display will start to
- * refresh at the new vsync period.
+ * @return timeline is the timeline for the vsync period change.
*/
- setActiveConfigAndVsyncPeriod(Display display, Config config,
- VsyncPeriodNanos vsyncPeriodNanos,
+ setActiveConfigWithConstraints(Display display, Config config,
VsyncPeriodChangeConstraints vsyncPeriodChangeConstraints)
- generates (Error error, int64_t newVsyncAppliedTime);
+ generates (Error error, VsyncPeriodChangeTimeline timeline);
};
diff --git a/graphics/composer/2.4/types.hal b/graphics/composer/2.4/types.hal
index b45d7a6..065f024 100644
--- a/graphics/composer/2.4/types.hal
+++ b/graphics/composer/2.4/types.hal
@@ -20,13 +20,36 @@
enum Error : @2.1::Error {
/**
- * Invalid vsync period
+ * Seamless cannot be required for configurations that don't share a config group
*/
- BAD_VSYNC_PERIOD = 9,
+ SEAMLESS_NOT_ALLOWED = 9,
/**
* Seamless requirements cannot be met
*/
SEAMLESS_NOT_POSSIBLE = 10,
};
+/**
+ * Timing for a vsync period change.
+ */
+struct VsyncPeriodChangeTimeline {
+ /**
+ * The time in CLOCK_MONOTONIC when the new display will start to refresh at
+ * the new vsync period.
+ */
+ int64_t newVsyncAppliedTimeNanos;
+
+ /**
+ * Set to true if the client is required to send a frame to be displayed before
+ * the change can take place.
+ */
+ bool refreshRequired;
+
+ /**
+ * The time in CLOCK_MONOTONIC when the client is expected to send the new frame.
+ * Should be ignored if refreshRequired is false.
+ */
+ int64_t refreshTimeNanos;
+};
+
typedef uint32_t VsyncPeriodNanos;
diff --git a/graphics/composer/2.4/utils/hal/include/composer-hal/2.4/ComposerClient.h b/graphics/composer/2.4/utils/hal/include/composer-hal/2.4/ComposerClient.h
index ddf209b..4160ed9 100644
--- a/graphics/composer/2.4/utils/hal/include/composer-hal/2.4/ComposerClient.h
+++ b/graphics/composer/2.4/utils/hal/include/composer-hal/2.4/ComposerClient.h
@@ -76,6 +76,13 @@
ALOGE_IF(!ret.isOk(), "failed to send onVsync_2_4: %s", ret.description().c_str());
}
+ void onVsyncPeriodTimingChanged(Display display,
+ const VsyncPeriodChangeTimeline& updatedTimeline) override {
+ auto ret = mCallback->onVsyncPeriodTimingChanged(display, updatedTimeline);
+ ALOGE_IF(!ret.isOk(), "failed to send onVsyncPeriodTimingChanged: %s",
+ ret.description().c_str());
+ }
+
protected:
const sp<IComposerCallback> mCallback;
V2_1::hal::ComposerResources* const mResources;
@@ -104,13 +111,12 @@
return Void();
}
- Return<void> getSupportedDisplayVsyncPeriods(
- Display display, Config config,
- IComposerClient::getSupportedDisplayVsyncPeriods_cb hidl_cb) override {
- std::vector<VsyncPeriodNanos> supportedVsyncPeriods;
- Error error =
- mHal->getSupportedDisplayVsyncPeriods(display, config, &supportedVsyncPeriods);
- hidl_cb(error, supportedVsyncPeriods);
+ Return<void> getDisplayAttribute_2_4(
+ Display display, Config config, IComposerClient::Attribute attribute,
+ IComposerClient::getDisplayAttribute_2_4_cb hidl_cb) override {
+ int32_t value = 0;
+ Error error = mHal->getDisplayAttribute_2_4(display, config, attribute, &value);
+ hidl_cb(error, value);
return Void();
}
@@ -122,15 +128,14 @@
return Void();
}
- Return<void> setActiveConfigAndVsyncPeriod(
- Display display, Config config, VsyncPeriodNanos vsyncPeriodNanos,
+ Return<void> setActiveConfigWithConstraints(
+ Display display, Config config,
const IComposerClient::VsyncPeriodChangeConstraints& vsyncPeriodChangeConstraints,
- IComposerClient::setActiveConfigAndVsyncPeriod_cb hidl_cb) override {
- int64_t newVsyncAppliedTime = 0;
- Error error = mHal->setActiveConfigAndVsyncPeriod(display, config, vsyncPeriodNanos,
- vsyncPeriodChangeConstraints,
- &newVsyncAppliedTime);
- hidl_cb(error, newVsyncAppliedTime);
+ IComposerClient::setActiveConfigWithConstraints_cb hidl_cb) override {
+ VsyncPeriodChangeTimeline timeline = {};
+ Error error = mHal->setActiveConfigWithConstraints(display, config,
+ vsyncPeriodChangeConstraints, &timeline);
+ hidl_cb(error, timeline);
return Void();
}
diff --git a/graphics/composer/2.4/utils/hal/include/composer-hal/2.4/ComposerHal.h b/graphics/composer/2.4/utils/hal/include/composer-hal/2.4/ComposerHal.h
index 0739f62..89dbe66 100644
--- a/graphics/composer/2.4/utils/hal/include/composer-hal/2.4/ComposerHal.h
+++ b/graphics/composer/2.4/utils/hal/include/composer-hal/2.4/ComposerHal.h
@@ -47,6 +47,8 @@
virtual void onVsync(Display display, int64_t timestamp) = 0;
virtual void onVsync_2_4(Display display, int64_t timestamp,
VsyncPeriodNanos vsyncPeriodNanos) = 0;
+ virtual void onVsyncPeriodTimingChanged(Display display,
+ const VsyncPeriodChangeTimeline& timeline) = 0;
};
virtual void registerEventCallback_2_4(EventCallback_2_4* callback) = 0;
@@ -57,13 +59,14 @@
Display display, std::vector<IComposerClient::DisplayCapability>* outCapabilities) = 0;
virtual Error getDisplayConnectionType(Display display,
IComposerClient::DisplayConnectionType* outType) = 0;
- virtual Error getSupportedDisplayVsyncPeriods(
- Display display, Config config, std::vector<VsyncPeriodNanos>* outVsyncPeriod) = 0;
+ virtual Error getDisplayAttribute_2_4(Display display, Config config,
+ IComposerClient::Attribute attribute,
+ int32_t* outValue) = 0;
virtual Error getDisplayVsyncPeriod(Display display, VsyncPeriodNanos* outVsyncPeriod) = 0;
- virtual Error setActiveConfigAndVsyncPeriod(
- Display display, Config config, VsyncPeriodNanos vsyncPeriodNanos,
+ virtual Error setActiveConfigWithConstraints(
+ Display display, Config config,
const IComposerClient::VsyncPeriodChangeConstraints& vsyncPeriodChangeConstraints,
- int64_t* outNewVsyncAppliedTime) = 0;
+ VsyncPeriodChangeTimeline* timeline) = 0;
};
} // namespace hal
diff --git a/graphics/composer/2.4/utils/passthrough/include/composer-passthrough/2.4/HwcHal.h b/graphics/composer/2.4/utils/passthrough/include/composer-passthrough/2.4/HwcHal.h
index 3420c8c..d59d0d5 100644
--- a/graphics/composer/2.4/utils/passthrough/include/composer-passthrough/2.4/HwcHal.h
+++ b/graphics/composer/2.4/utils/passthrough/include/composer-passthrough/2.4/HwcHal.h
@@ -25,6 +25,7 @@
#include <android/hardware/graphics/composer/2.4/IComposerClient.h>
#include <composer-hal/2.4/ComposerHal.h>
#include <composer-passthrough/2.3/HwcHal.h>
+#include <hardware/hwcomposer_defs.h>
namespace android {
namespace hardware {
@@ -51,14 +52,22 @@
void registerEventCallback_2_4(hal::ComposerHal::EventCallback_2_4* callback) override {
mEventCallback_2_4 = callback;
- mDispatch.registerCallback(mDevice, HWC2_CALLBACK_HOTPLUG, this,
- reinterpret_cast<hwc2_function_pointer_t>(hotplugHook));
- mDispatch.registerCallback(mDevice, HWC2_CALLBACK_REFRESH, this,
- reinterpret_cast<hwc2_function_pointer_t>(refreshHook));
- mDispatch.registerCallback(mDevice, HWC2_CALLBACK_VSYNC, this,
- reinterpret_cast<hwc2_function_pointer_t>(vsyncHook));
- mDispatch.registerCallback(mDevice, HWC2_CALLBACK_VSYNC_2_4, this,
- reinterpret_cast<hwc2_function_pointer_t>(vsync_2_4_Hook));
+ BaseType2_1::mDispatch.registerCallback(
+ mDevice, HWC2_CALLBACK_HOTPLUG, this,
+ reinterpret_cast<hwc2_function_pointer_t>(hotplugHook));
+ BaseType2_1::mDispatch.registerCallback(
+ mDevice, HWC2_CALLBACK_REFRESH, this,
+ reinterpret_cast<hwc2_function_pointer_t>(refreshHook));
+ BaseType2_1::mDispatch.registerCallback(
+ mDevice, HWC2_CALLBACK_VSYNC, this,
+ reinterpret_cast<hwc2_function_pointer_t>(vsyncHook));
+ BaseType2_1::mDispatch.registerCallback(
+ mDevice, HWC2_CALLBACK_VSYNC_2_4, this,
+ reinterpret_cast<hwc2_function_pointer_t>(vsync_2_4_Hook));
+
+ BaseType2_1::mDispatch.registerCallback(
+ mDevice, HWC2_CALLBACK_VSYNC_PERIOD_TIMING_CHANGED, this,
+ reinterpret_cast<hwc2_function_pointer_t>(vsyncPeriodTimingChangedHook));
}
void unregisterEventCallback_2_4() override {
@@ -69,10 +78,12 @@
// - will never be called afterward
//
// which is likely incorrect
- mDispatch.registerCallback(mDevice, HWC2_CALLBACK_HOTPLUG, this, nullptr);
- mDispatch.registerCallback(mDevice, HWC2_CALLBACK_REFRESH, this, nullptr);
- mDispatch.registerCallback(mDevice, HWC2_CALLBACK_VSYNC, this, nullptr);
- mDispatch.registerCallback(mDevice, HWC2_CALLBACK_VSYNC_2_4, this, nullptr);
+ BaseType2_1::mDispatch.registerCallback(mDevice, HWC2_CALLBACK_HOTPLUG, this, nullptr);
+ BaseType2_1::mDispatch.registerCallback(mDevice, HWC2_CALLBACK_REFRESH, this, nullptr);
+ BaseType2_1::mDispatch.registerCallback(mDevice, HWC2_CALLBACK_VSYNC, this, nullptr);
+ BaseType2_1::mDispatch.registerCallback(mDevice, HWC2_CALLBACK_VSYNC_2_4, this, nullptr);
+ BaseType2_1::mDispatch.registerCallback(mDevice, HWC2_CALLBACK_VSYNC_PERIOD_TIMING_CHANGED,
+ this, nullptr);
mEventCallback_2_4 = nullptr;
}
@@ -106,26 +117,16 @@
return static_cast<Error>(error);
}
- Error getSupportedDisplayVsyncPeriods(Display display, Config config,
- std::vector<VsyncPeriodNanos>* outVsyncPeriods) override {
- if (!mDispatch.getSupportedDisplayVsyncPeriods) {
- return Error::UNSUPPORTED;
+ Error getDisplayAttribute_2_4(Display display, Config config,
+ IComposerClient::Attribute attribute,
+ int32_t* outValue) override {
+ int32_t err = BaseType2_1::mDispatch.getDisplayAttribute(
+ mDevice, display, config, static_cast<int32_t>(attribute), outValue);
+ if (err != HWC2_ERROR_NONE && *outValue == -1) {
+ // Convert the error from hwcomposer2 to IComposerClient definition
+ return Error::BAD_PARAMETER;
}
-
- uint32_t count = 0;
- int32_t error = mDispatch.getSupportedDisplayVsyncPeriods(mDevice, display, config, &count,
- nullptr);
- if (error != HWC2_ERROR_NONE) {
- return static_cast<Error>(error);
- }
- outVsyncPeriods->resize(count);
- error = mDispatch.getSupportedDisplayVsyncPeriods(mDevice, display, config, &count,
- outVsyncPeriods->data());
- if (error != HWC2_ERROR_NONE) {
- *outVsyncPeriods = std::vector<VsyncPeriodNanos>();
- return static_cast<Error>(error);
- }
- return Error::NONE;
+ return static_cast<Error>(err);
}
Error getDisplayVsyncPeriod(Display display, VsyncPeriodNanos* outVsyncPeriod) override {
@@ -140,11 +141,11 @@
return Error::NONE;
}
- Error setActiveConfigAndVsyncPeriod(
- Display display, Config config, VsyncPeriodNanos vsyncPeriodNanos,
+ Error setActiveConfigWithConstraints(
+ Display display, Config config,
const IComposerClient::VsyncPeriodChangeConstraints& vsyncPeriodChangeConstraints,
- int64_t* outNewVsyncAppliedTime) override {
- if (!mDispatch.setActiveConfigAndVsyncPeriod) {
+ VsyncPeriodChangeTimeline* timeline) override {
+ if (!mDispatch.setActiveConfigWithConstraints) {
return Error::UNSUPPORTED;
}
@@ -154,12 +155,15 @@
vsync_period_change_constraints.seamlessRequired =
vsyncPeriodChangeConstraints.seamlessRequired;
- int32_t error = mDispatch.setActiveConfigAndVsyncPeriod(
- mDevice, display, config, vsyncPeriodNanos, &vsync_period_change_constraints,
- outNewVsyncAppliedTime);
+ hwc_vsync_period_change_timeline_t out_timeline;
+ int32_t error = mDispatch.setActiveConfigWithConstraints(
+ mDevice, display, config, &vsync_period_change_constraints, &out_timeline);
if (error != HWC2_ERROR_NONE) {
return static_cast<Error>(error);
}
+ timeline->newVsyncAppliedTimeNanos = out_timeline.newVsyncAppliedTimeNanos;
+ timeline->refreshRequired = out_timeline.refreshRequired;
+ timeline->refreshTimeNanos = out_timeline.refreshTimeNanos;
return Error::NONE;
}
@@ -171,13 +175,10 @@
this->initOptionalDispatch(HWC2_FUNCTION_GET_DISPLAY_CONNECTION_TYPE,
&mDispatch.getDisplayConnectionType);
- this->initOptionalDispatch(HWC2_FUNCTION_REGISTER_CALLBACK, &mDispatch.registerCallback);
- this->initOptionalDispatch(HWC2_FUNCTION_GET_SUPPORTED_DISPLAY_VSYNC_PERIODS,
- &mDispatch.getSupportedDisplayVsyncPeriods);
this->initOptionalDispatch(HWC2_FUNCTION_GET_DISPLAY_VSYNC_PERIOD,
&mDispatch.getDisplayVsyncPeriod);
- this->initOptionalDispatch(HWC2_FUNCTION_SET_ACTIVE_CONFIG_AND_VSYNC_PERIOD,
- &mDispatch.setActiveConfigAndVsyncPeriod);
+ this->initOptionalDispatch(HWC2_FUNCTION_SET_ACTIVE_CONFIG_WITH_CONSTRAINTS,
+ &mDispatch.setActiveConfigWithConstraints);
return true;
}
@@ -205,13 +206,22 @@
hal->mEventCallback_2_4->onVsync_2_4(display, timestamp, vsyncPeriodNanos);
}
+ static void vsyncPeriodTimingChangedHook(hwc2_callback_data_t callbackData,
+ hwc2_display_t display,
+ hwc_vsync_period_change_timeline_t* updated_timeline) {
+ auto hal = static_cast<HwcHalImpl*>(callbackData);
+ VsyncPeriodChangeTimeline timeline;
+ timeline.newVsyncAppliedTimeNanos = updated_timeline->newVsyncAppliedTimeNanos;
+ timeline.refreshRequired = updated_timeline->refreshRequired;
+ timeline.refreshTimeNanos = updated_timeline->refreshTimeNanos;
+ hal->mEventCallback_2_4->onVsyncPeriodTimingChanged(display, timeline);
+ }
+
private:
struct {
HWC2_PFN_GET_DISPLAY_CONNECTION_TYPE getDisplayConnectionType;
- HWC2_PFN_REGISTER_CALLBACK registerCallback;
- HWC2_PFN_GET_SUPPORTED_DISPLAY_VSYNC_PERIODS getSupportedDisplayVsyncPeriods;
HWC2_PFN_GET_DISPLAY_VSYNC_PERIOD getDisplayVsyncPeriod;
- HWC2_PFN_SET_ACTIVE_CONFIG_AND_VSYNC_PERIOD setActiveConfigAndVsyncPeriod;
+ HWC2_PFN_SET_ACTIVE_CONFIG_WITH_CONSTRAINTS setActiveConfigWithConstraints;
} mDispatch = {};
hal::ComposerHal::EventCallback_2_4* mEventCallback_2_4 = nullptr;
diff --git a/graphics/composer/2.4/utils/vts/ComposerVts.cpp b/graphics/composer/2.4/utils/vts/ComposerVts.cpp
index b02a59a..35ac23f 100644
--- a/graphics/composer/2.4/utils/vts/ComposerVts.cpp
+++ b/graphics/composer/2.4/utils/vts/ComposerVts.cpp
@@ -70,15 +70,18 @@
return error;
}
-Error ComposerClient::getSupportedDisplayVsyncPeriods(
- Display display, Config config, std::vector<VsyncPeriodNanos>* outSupportedVsyncPeriods) {
- Error error = Error::NONE;
- mClient->getSupportedDisplayVsyncPeriods(
- display, config, [&](const auto& tmpError, const auto& tmpSupportedVsyncPeriods) {
- error = tmpError;
- *outSupportedVsyncPeriods = tmpSupportedVsyncPeriods;
+int32_t ComposerClient::getDisplayAttribute_2_4(
+ android::hardware::graphics::composer::V2_1::Display display,
+ android::hardware::graphics::composer::V2_1::Config config,
+ IComposerClient::Attribute attribute) {
+ int32_t value = 0;
+ mClient->getDisplayAttribute_2_4(
+ display, config, attribute, [&](const auto& tmpError, const auto& tmpValue) {
+ ASSERT_EQ(Error::NONE, tmpError) << "failed to get display attribute";
+ value = tmpValue;
});
- return error;
+
+ return value;
}
Error ComposerClient::getDisplayVsyncPeriod(Display display, VsyncPeriodNanos* outVsyncPeriod) {
@@ -90,17 +93,16 @@
return error;
}
-Error ComposerClient::setActiveConfigAndVsyncPeriod(
- Display display, Config config, VsyncPeriodNanos vsyncPeriodNanos,
+Error ComposerClient::setActiveConfigWithConstraints(
+ Display display, Config config,
const IComposerClient::VsyncPeriodChangeConstraints& vsyncPeriodChangeConstraints,
- int64_t* outNewVsyncAppliedTime) {
+ VsyncPeriodChangeTimeline* timeline) {
Error error = Error::NONE;
- mClient->setActiveConfigAndVsyncPeriod(
- display, config, vsyncPeriodNanos, vsyncPeriodChangeConstraints,
- [&](const auto& tmpError, const auto& tmpNewVsyncAppliedTime) {
- error = tmpError;
- *outNewVsyncAppliedTime = tmpNewVsyncAppliedTime;
- });
+ mClient->setActiveConfigWithConstraints(display, config, vsyncPeriodChangeConstraints,
+ [&](const auto& tmpError, const auto& tmpTimeline) {
+ error = tmpError;
+ *timeline = tmpTimeline;
+ });
return error;
}
diff --git a/graphics/composer/2.4/utils/vts/include/composer-vts/2.4/ComposerVts.h b/graphics/composer/2.4/utils/vts/include/composer-vts/2.4/ComposerVts.h
index 5db3e16..83e74ed 100644
--- a/graphics/composer/2.4/utils/vts/include/composer-vts/2.4/ComposerVts.h
+++ b/graphics/composer/2.4/utils/vts/include/composer-vts/2.4/ComposerVts.h
@@ -74,15 +74,15 @@
Error getDisplayConnectionType(Display display,
IComposerClient::DisplayConnectionType* outType);
- Error getSupportedDisplayVsyncPeriods(Display display, Config config,
- std::vector<VsyncPeriodNanos>* outSupportedVsyncPeriods);
+ int32_t getDisplayAttribute_2_4(Display display, Config config,
+ IComposerClient::Attribute attribute);
Error getDisplayVsyncPeriod(Display display, VsyncPeriodNanos* outVsyncPeriods);
- Error setActiveConfigAndVsyncPeriod(
- Display display, Config config, VsyncPeriodNanos vsyncPeriodNanos,
+ Error setActiveConfigWithConstraints(
+ Display display, Config config,
const IComposerClient::VsyncPeriodChangeConstraints& vsyncPeriodChangeConstraints,
- int64_t* outNewVsyncAppliedTime);
+ VsyncPeriodChangeTimeline* timeline);
private:
const sp<IComposerClient> mClient;
diff --git a/graphics/composer/2.4/vts/functional/VtsHalGraphicsComposerV2_4TargetTest.cpp b/graphics/composer/2.4/vts/functional/VtsHalGraphicsComposerV2_4TargetTest.cpp
index 2c87666..f038f55 100644
--- a/graphics/composer/2.4/vts/functional/VtsHalGraphicsComposerV2_4TargetTest.cpp
+++ b/graphics/composer/2.4/vts/functional/VtsHalGraphicsComposerV2_4TargetTest.cpp
@@ -114,7 +114,7 @@
void execute() { mComposerClient->execute(mReader.get(), mWriter.get()); }
- void Test_setActiveConfigAndVsyncPeriod(
+ void Test_setActiveConfigWithConstraints(
const IComposerClient::VsyncPeriodChangeConstraints& constraints);
std::unique_ptr<Composer> mComposer;
@@ -194,42 +194,28 @@
}
}
-TEST_P(GraphicsComposerHidlTest, getSupportedDisplayVsyncPeriods_BadDisplay) {
- std::vector<VsyncPeriodNanos> supportedVsyncPeriods;
- EXPECT_EQ(Error::BAD_DISPLAY, mComposerClient->getSupportedDisplayVsyncPeriods(
- mInvalidDisplayId, Config(0), &supportedVsyncPeriods));
-}
+TEST_P(GraphicsComposerHidlTest, GetDisplayAttribute_2_4) {
+ std::vector<Config> configs = mComposerClient->getDisplayConfigs(mPrimaryDisplay);
+ for (auto config : configs) {
+ const std::array<IComposerClient::Attribute, 4> requiredAttributes = {{
+ IComposerClient::Attribute::WIDTH,
+ IComposerClient::Attribute::HEIGHT,
+ IComposerClient::Attribute::VSYNC_PERIOD,
+ IComposerClient::Attribute::CONFIG_GROUP,
+ }};
+ for (auto attribute : requiredAttributes) {
+ mComposerClient->getDisplayAttribute_2_4(mPrimaryDisplay, config, attribute);
+ }
-TEST_P(GraphicsComposerHidlTest, getSupportedDisplayVsyncPeriods_BadConfig) {
- for (Display display : mComposerCallback->getDisplays()) {
- Config invalidConfigId = GetInvalidConfigId(display);
- std::vector<VsyncPeriodNanos> supportedVsyncPeriods;
- EXPECT_EQ(Error::BAD_CONFIG, mComposerClient->getSupportedDisplayVsyncPeriods(
- display, invalidConfigId, &supportedVsyncPeriods));
- }
-}
-
-TEST_P(GraphicsComposerHidlTest, getSupportedDisplayVsyncPeriods) {
- for (Display display : mComposerCallback->getDisplays()) {
- for (Config config : mComposerClient->getDisplayConfigs(display)) {
- std::vector<VsyncPeriodNanos> supportedVsyncPeriods;
-
- // Get the default vsync period from the config
- VsyncPeriodNanos defaultVsyncPeiord = mComposerClient->getDisplayAttribute(
- display, config, IComposerClient::Attribute::VSYNC_PERIOD);
- // Get all supported vsync periods for this config
- EXPECT_EQ(Error::NONE, mComposerClient->getSupportedDisplayVsyncPeriods(
- display, config, &supportedVsyncPeriods));
- // Default vsync period must be present in the list
- EXPECT_NE(std::find(supportedVsyncPeriods.begin(), supportedVsyncPeriods.end(),
- defaultVsyncPeiord),
- supportedVsyncPeriods.end());
-
- // Each vsync period must be unique
- std::unordered_set<VsyncPeriodNanos> vsyncPeriodSet;
- for (VsyncPeriodNanos vsyncPeriodNanos : supportedVsyncPeriods) {
- EXPECT_TRUE(vsyncPeriodSet.insert(vsyncPeriodNanos).second);
- }
+ const std::array<IComposerClient::Attribute, 2> optionalAttributes = {{
+ IComposerClient::Attribute::DPI_X,
+ IComposerClient::Attribute::DPI_Y,
+ }};
+ for (auto attribute : optionalAttributes) {
+ mComposerClient->getRaw()->getDisplayAttribute_2_4(
+ mPrimaryDisplay, config, attribute, [&](const auto& tmpError, const auto&) {
+ EXPECT_TRUE(tmpError == Error::NONE || tmpError == Error::UNSUPPORTED);
+ });
}
}
}
@@ -240,20 +226,40 @@
mComposerClient->getDisplayVsyncPeriod(mInvalidDisplayId, &vsyncPeriodNanos));
}
-TEST_P(GraphicsComposerHidlTest, setActiveConfigAndVsyncPeriod_BadDisplay) {
- int64_t newVsyncAppliedTime;
+TEST_P(GraphicsComposerHidlTest, getDisplayVsyncPeriod) {
+ for (Display display : mComposerCallback->getDisplays()) {
+ for (Config config : mComposerClient->getDisplayConfigs(display)) {
+ mComposerClient->setActiveConfig(display, config);
+
+ VsyncPeriodNanos vsyncPeriodNanos;
+ VsyncPeriodNanos expectedvsyncPeriodNanos = mComposerClient->getDisplayAttribute_2_4(
+ display, config, IComposerClient::IComposerClient::Attribute::VSYNC_PERIOD);
+ int retryCount = 100;
+ do {
+ std::this_thread::sleep_for(10ms);
+ EXPECT_EQ(Error::NONE,
+ mComposerClient->getDisplayVsyncPeriod(display, &vsyncPeriodNanos));
+ --retryCount;
+ } while (retryCount > 0);
+
+ EXPECT_EQ(vsyncPeriodNanos, expectedvsyncPeriodNanos);
+ }
+ }
+}
+
+TEST_P(GraphicsComposerHidlTest, setActiveConfigWithConstraints_BadDisplay) {
+ VsyncPeriodChangeTimeline timeline;
IComposerClient::VsyncPeriodChangeConstraints constraints;
constraints.seamlessRequired = false;
constraints.desiredTimeNanos = systemTime();
- EXPECT_EQ(Error::BAD_DISPLAY, mComposerClient->setActiveConfigAndVsyncPeriod(
- mInvalidDisplayId, Config(0), VsyncPeriodNanos(0),
- constraints, &newVsyncAppliedTime));
+ EXPECT_EQ(Error::BAD_DISPLAY, mComposerClient->setActiveConfigWithConstraints(
+ mInvalidDisplayId, Config(0), constraints, &timeline));
}
-TEST_P(GraphicsComposerHidlTest, setActiveConfigAndVsyncPeriod_BadConfig) {
- int64_t newVsyncAppliedTime;
+TEST_P(GraphicsComposerHidlTest, setActiveConfigWithConstraints_BadConfig) {
+ VsyncPeriodChangeTimeline timeline;
IComposerClient::VsyncPeriodChangeConstraints constraints;
constraints.seamlessRequired = false;
@@ -261,93 +267,114 @@
for (Display display : mComposerCallback->getDisplays()) {
Config invalidConfigId = GetInvalidConfigId(display);
- EXPECT_EQ(Error::BAD_CONFIG, mComposerClient->setActiveConfigAndVsyncPeriod(
- display, invalidConfigId, VsyncPeriodNanos(0),
- constraints, &newVsyncAppliedTime));
+ EXPECT_EQ(Error::BAD_CONFIG, mComposerClient->setActiveConfigWithConstraints(
+ display, invalidConfigId, constraints, &timeline));
}
}
-TEST_P(GraphicsComposerHidlTest, setActiveConfigAndVsyncPeriod_BadVsyncPeriod) {
- int64_t newVsyncAppliedTime;
+TEST_P(GraphicsComposerHidlTest, setActiveConfigWithConstraints_SeamlessNotAllowed) {
+ VsyncPeriodChangeTimeline timeline;
IComposerClient::VsyncPeriodChangeConstraints constraints;
- constraints.seamlessRequired = false;
+ constraints.seamlessRequired = true;
constraints.desiredTimeNanos = systemTime();
for (Display display : mComposerCallback->getDisplays()) {
for (Config config : mComposerClient->getDisplayConfigs(display)) {
- EXPECT_EQ(Error::BAD_VSYNC_PERIOD, mComposerClient->setActiveConfigAndVsyncPeriod(
- display, config, VsyncPeriodNanos(0),
- constraints, &newVsyncAppliedTime));
- }
- }
-}
+ int32_t configGroup = mComposerClient->getDisplayAttribute_2_4(
+ display, config, IComposerClient::IComposerClient::Attribute::CONFIG_GROUP);
-void GraphicsComposerHidlTest::Test_setActiveConfigAndVsyncPeriod(
- const IComposerClient::VsyncPeriodChangeConstraints& constraints) {
- int64_t newVsyncAppliedTime;
-
- for (Display display : mComposerCallback->getDisplays()) {
- for (Config config : mComposerClient->getDisplayConfigs(display)) {
- std::vector<VsyncPeriodNanos> supportedVsyncPeriods;
-
- EXPECT_EQ(Error::NONE, mComposerClient->getSupportedDisplayVsyncPeriods(
- display, config, &supportedVsyncPeriods));
- for (VsyncPeriodNanos newVsyncPeriod : supportedVsyncPeriods) {
- VsyncPeriodNanos vsyncPeriodNanos;
- EXPECT_EQ(Error::NONE,
- mComposerClient->getDisplayVsyncPeriod(display, &vsyncPeriodNanos));
-
- if (vsyncPeriodNanos == newVsyncPeriod) {
- continue;
+ for (Config otherConfig : mComposerClient->getDisplayConfigs(display)) {
+ int32_t otherConfigGroup = mComposerClient->getDisplayAttribute_2_4(
+ display, otherConfig,
+ IComposerClient::IComposerClient::Attribute::CONFIG_GROUP);
+ if (configGroup != otherConfigGroup) {
+ mComposerClient->setActiveConfig(display, config);
+ EXPECT_EQ(Error::SEAMLESS_NOT_ALLOWED,
+ mComposerClient->setActiveConfigWithConstraints(
+ display, otherConfig, constraints, &timeline));
}
-
- EXPECT_EQ(Error::NONE, mComposerClient->setActiveConfigAndVsyncPeriod(
- display, config, newVsyncPeriod, constraints,
- &newVsyncAppliedTime));
-
- EXPECT_TRUE(newVsyncAppliedTime >= constraints.desiredTimeNanos);
-
- // Refresh rate should change within a reasonable time
- constexpr nsecs_t kReasonableTimeForChange = 1'000'000'000; // 1 second
- EXPECT_TRUE(newVsyncAppliedTime - constraints.desiredTimeNanos <=
- kReasonableTimeForChange);
-
- while (systemTime() <= newVsyncAppliedTime) {
- EXPECT_EQ(Error::NONE,
- mComposerClient->getDisplayVsyncPeriod(display, &vsyncPeriodNanos));
- if (systemTime() <= constraints.desiredTimeNanos) {
- EXPECT_NE(vsyncPeriodNanos, newVsyncPeriod);
- }
-
- if (vsyncPeriodNanos == newVsyncPeriod) {
- break;
- }
- std::this_thread::sleep_for(10ms);
- }
- EXPECT_EQ(Error::NONE,
- mComposerClient->getDisplayVsyncPeriod(display, &vsyncPeriodNanos));
- EXPECT_EQ(vsyncPeriodNanos, newVsyncPeriod);
}
}
}
}
-TEST_P(GraphicsComposerHidlTest, setActiveConfigAndVsyncPeriod) {
+void GraphicsComposerHidlTest::Test_setActiveConfigWithConstraints(
+ const IComposerClient::VsyncPeriodChangeConstraints& constraints) {
+ VsyncPeriodChangeTimeline timeline = {};
+
+ for (Display display : mComposerCallback->getDisplays()) {
+ for (Config config : mComposerClient->getDisplayConfigs(display)) {
+ mComposerClient->setActiveConfig(display, config);
+
+ int32_t configVsyncPeriod = mComposerClient->getDisplayAttribute_2_4(
+ display, config, IComposerClient::IComposerClient::Attribute::VSYNC_PERIOD);
+ for (Config otherConfig : mComposerClient->getDisplayConfigs(display)) {
+ if (config == otherConfig) {
+ continue;
+ }
+
+ int32_t otherVsyncPeriod = mComposerClient->getDisplayAttribute_2_4(
+ display, otherConfig,
+ IComposerClient::IComposerClient::Attribute::VSYNC_PERIOD);
+
+ if (configVsyncPeriod == otherVsyncPeriod) {
+ continue;
+ }
+
+ EXPECT_EQ(Error::NONE, mComposerClient->setActiveConfigWithConstraints(
+ display, otherConfig, constraints, &timeline));
+
+ if (timeline.refreshRequired) {
+ // TODO(b/143775556): handle this case;
+ continue;
+ }
+
+ EXPECT_TRUE(timeline.newVsyncAppliedTimeNanos >= constraints.desiredTimeNanos);
+
+ // Refresh rate should change within a reasonable time
+ constexpr nsecs_t kReasonableTimeForChange = 1'000'000'000; // 1 second
+ EXPECT_TRUE(timeline.newVsyncAppliedTimeNanos - constraints.desiredTimeNanos <=
+ kReasonableTimeForChange);
+
+ while (systemTime() <= timeline.newVsyncAppliedTimeNanos) {
+ VsyncPeriodNanos vsyncPeriodNanos;
+ EXPECT_EQ(Error::NONE,
+ mComposerClient->getDisplayVsyncPeriod(display, &vsyncPeriodNanos));
+
+ if (systemTime() <= constraints.desiredTimeNanos) {
+ EXPECT_NE(vsyncPeriodNanos, otherVsyncPeriod);
+ }
+
+ if (vsyncPeriodNanos == otherVsyncPeriod) {
+ break;
+ }
+ std::this_thread::sleep_for(10ms);
+ }
+ VsyncPeriodNanos vsyncPeriodNanos;
+ EXPECT_EQ(Error::NONE,
+ mComposerClient->getDisplayVsyncPeriod(display, &vsyncPeriodNanos));
+ EXPECT_EQ(vsyncPeriodNanos, otherVsyncPeriod);
+ }
+ }
+ }
+}
+
+TEST_P(GraphicsComposerHidlTest, setActiveConfigWithConstraints) {
IComposerClient::VsyncPeriodChangeConstraints constraints;
constraints.seamlessRequired = false;
constraints.desiredTimeNanos = systemTime();
- Test_setActiveConfigAndVsyncPeriod(constraints);
+ Test_setActiveConfigWithConstraints(constraints);
}
-TEST_P(GraphicsComposerHidlTest, setActiveConfigAndVsyncPeriod_delayed) {
+TEST_P(GraphicsComposerHidlTest, setActiveConfigWithConstraints_delayed) {
IComposerClient::VsyncPeriodChangeConstraints constraints;
constexpr auto kDelayForChange = 300ms;
constraints.seamlessRequired = false;
constraints.desiredTimeNanos = systemTime() + kDelayForChange.count();
- Test_setActiveConfigAndVsyncPeriod(constraints);
+ Test_setActiveConfigWithConstraints(constraints);
}
INSTANTIATE_TEST_SUITE_P(
diff --git a/graphics/mapper/4.0/IMapper.hal b/graphics/mapper/4.0/IMapper.hal
index 640539f..a1f07a4 100644
--- a/graphics/mapper/4.0/IMapper.hal
+++ b/graphics/mapper/4.0/IMapper.hal
@@ -35,18 +35,20 @@
*/
uint32_t width;
- /**
- * The height specifies how many rows of pixels must be in the
- * allocated buffer.
- */
+ /**
+ * The height specifies how many rows of pixels must be in the
+ * allocated buffer.
+ */
uint32_t height;
- /**
- * The number of image layers that must be in the allocated buffer.
- */
+ /**
+ * The number of image layers that must be in the allocated buffer.
+ */
uint32_t layerCount;
- /** Buffer pixel format. */
+ /**
+ * Buffer pixel format.
+ */
PixelFormat format;
/**
@@ -197,16 +199,20 @@
* outside of @p accessRegion is undefined, except that it must not cause
* process termination.
*
+ * This function can lock both single-planar and multi-planar formats. The caller
+ * should use get() to get information about the buffer they are locking.
+ * get() can be used to get information about the planes, offsets, stride,
+ * etc.
+ *
+ * This function must also work on buffers with
+ * `AHARDWAREBUFFER_FORMAT_Y8Cb8Cr8_*` if supported by the device, as well
+ * as with any other formats requested by multimedia codecs when they are
+ * configured with a flexible-YUV-compatible color format.
+ *
* On success, @p data must be filled with a pointer to the locked buffer
* memory. This address will represent the top-left corner of the entire
* buffer, even if @p accessRegion does not begin at the top-left corner.
*
- * On success, bytesPerPixel must contain the number of bytes per pixel in
- * the buffer. If the bytesPerPixel is unknown or variable, a value of -1
- * should be returned. bytesPerStride must contain the bytes per stride of
- * the buffer. If the bytesPerStride is unknown or variable, a value of -1
- * should be returned.
- *
* The locked buffer must adhere to the format requested at allocation time
* in the BufferDescriptorInfo.
*
@@ -229,55 +235,13 @@
* - `NO_RESOURCES` if the buffer cannot be locked at this time. Note
* that locking may succeed at a later time.
* @return data CPU-accessible pointer to the buffer data.
- * @return bytesPerPixel the number of bytes per pixel in the buffer
- * @return bytesPerStride the number of bytes per stride of the buffer
*/
lock(pointer buffer,
uint64_t cpuUsage,
Rect accessRegion,
handle acquireFence)
generates (Error error,
- pointer data,
- int32_t bytesPerPixel,
- int32_t bytesPerStride);
-
- /**
- * Locks a YCbCr buffer for the specified CPU usage.
- *
- * This is largely the same as lock(), except that instead of returning a
- * pointer directly to the buffer data, it returns a `YCbCrLayout` struct
- * describing how to access the data planes.
- *
- * This function must work on buffers with
- * `AHARDWAREBUFFER_FORMAT_Y8Cb8Cr8_*` if supported by the device, as well
- * as with any other formats requested by multimedia codecs when they are
- * configured with a flexible-YUV-compatible color format.
- *
- * @param buffer Buffer to lock.
- * @param cpuUsage CPU usage flags to request. See +ndk
- * libnativewindow#AHardwareBuffer_UsageFlags for possible values.
- * @param accessRegion Portion of the buffer that the client intends to
- * access.
- * @param acquireFence Handle containing a file descriptor referring to a
- * sync fence object, which will be signaled when it is safe for the
- * mapper to lock the buffer. @p acquireFence may be empty if it is
- * already safe to lock.
- * @return error Error status of the call, which may be
- * - `NONE` upon success.
- * - `BAD_BUFFER` if the buffer is invalid or is incompatible with this
- * function.
- * - `BAD_VALUE` if @p cpuUsage is 0, contains non-CPU usage flags, or
- * is incompatible with the buffer.
- * - `NO_RESOURCES` if the buffer cannot be locked at this time. Note
- * that locking may succeed at a later time.
- * @return layout Data layout of the locked buffer.
- */
- lockYCbCr(pointer buffer,
- uint64_t cpuUsage,
- Rect accessRegion,
- handle acquireFence)
- generates (Error error,
- YCbCrLayout layout);
+ pointer data);
/**
* Unlocks a buffer to indicate all CPU accesses to the buffer have
@@ -309,5 +273,247 @@
generates (Error error,
bool supported);
+
+ /**
+ * Description for get(...), set(...) and getFromBufferDescriptorInfo(...)
+ *
+ * ------------ Overview -----------------------------------
+ * Gralloc 4 adds support for getting and setting buffer metadata on a buffer.
+ *
+ * To get buffer metadata, the client passes in a buffer handle and a token that
+ * represents the type of buffer metadata they would like to get. IMapper returns
+ * a byte stream that contains the buffer metadata. To set the buffer metadata, the
+ * client passes in a buffer handle and a token that represents the type of buffer
+ * metadata they would like to set and a byte stream that contains the buffer metadata
+ * they are setting.
+ *
+ * Buffer metadata is global for a buffer. When the metadata is set on the buffer
+ * in a process, the updated metadata should be available to all other processes.
+ * Please see "Storing and Propagating Metadata" below for more details.
+ *
+ * The getter and setter functions have been optimized for easy vendor extension.
+ * They do not require a formal HIDL extension to add support for getting and setting
+ * vendor defined buffer metadata. In order to allow easy extension, the types used
+ * here are not typical HIDL types. See "Buffer Metadata Token" and
+ * "Buffer Metadata Stream" below for more details.
+ *
+ * ------------ Storing and Propagating Metadata -----------
+ * Buffer metadata must be global. Any changes to the metadata must be propagated
+ * to all other processes immediately. Vendors may chose how they would like support
+ * this functionality.
+ *
+ * We recommend supporting this functionality by allocating an extra page of shared
+ * memory and storing it in the buffer's native_handle_t. The buffer metadata can
+ * be stored in the extra page of shared memory. Set operations are automatically
+ * propagated to all other processes.
+ *
+ * ------------ Buffer Metadata Synchronization ------------
+ * There are no explicit buffer metadata synchronization primitives. Many devices
+ * before gralloc 4 already support getting and setting of global buffer metadata
+ * with no explicit synchronization primitives. Adding synchronization primitives
+ * would just add unnecessary complexity.
+ *
+ * The general rule is if a process has permission to write to a buffer, they
+ * have permission to write to the buffer's metadata. If a process has permission
+ * to read from a buffer, they have permission to read the buffer's metadata.
+ *
+ * There is one exception to this rule. Fences CANNOT be used to protect a buffer's
+ * metadata. A process should finish writing to a buffer's metadata before
+ * sending the buffer to another process that will read or write to the buffer.
+ * This exception is needed because sometimes userspace needs to read the
+ * buffer's metadata before the buffer's contents are ready.
+ *
+ * As a simple example: an app renders to a buffer and then displays the buffer.
+ * In this example when the app renders to the buffer, both the buffer and its
+ * metadata need to be updated. The app's process queues up its work on the GPU
+ * and gets back an acquire fence. The app's process must update the buffer's
+ * metadata before enqueuing the buffer to SurfaceFlinger. The app process CANNOT
+ * update the buffer's metadata after enqueuing the buffer. When HardwareComposer
+ * receives the buffer, it is immediately safe to read the buffer's metadata
+ * and use it to program the display driver. To read the buffer's contents,
+ * display driver must still wait on the acquire fence.
+ *
+ * ------------ Buffer Metadata Token ----------------------
+ * In order to allow arbitrary vendor defined metadata, we could not use a
+ * HIDL enum as the buffer metadata token. Extending a HIDL enum requires a full
+ * HIDL extension. We also could not use a simple non-HIDL enum because vendor
+ * defined enums from different vendors could collide. Instead we have defined
+ * a struct that has a string representing the enum type and an int that
+ * represents the enum value. The string protects different enum values from
+ * colliding.
+ *
+ * The token struct (MetadataType) is defined as a HIDL struct since it
+ * is passed into a HIDL function. The standard buffer metadata types are NOT
+ * defined as a HIDL enum because it would have required a new IMapper version
+ * just to add future standard buffer metadata types. By putting the enum in the
+ * stable AIDL (hardware/interfaces/graphics/common/aidl/android/hardware/
+ * graphics/common/StandardMetadataType.aidl), vendors will be able to optionally
+ * choose to support future standard buffer metadata types without upgrading
+ * HIDL versions. For more information see the description of "struct MetadataType".
+ *
+ * ------------ Buffer Metadata Stream ---------------------
+ * The buffer metadata is get and set as a byte stream (vec<uint8_t>). By getting
+ * and setting buffer metadata as a byte stream, vendors can use the standard
+ * getters and setter functions defined here. Vendors do NOT need to add their own
+ * getters and setter functions for each new type of buffer metadata.
+ *
+ * Converting buffer metadata into a byte stream can be non-trivial. For the standard
+ * buffer metadata types defined in StandardMetadataType.aidl, there are also
+ * support functions that will encode the buffer metadata into a byte stream
+ * and decode the buffer metadata from a byte stream. We STRONGLY recommend using
+ * these support functions. The framework will use them when getting and setting
+ * metadata. The support functions are defined in
+ * frameworks/native/libs/gralloc/types/include/gralloctypes/Gralloc4.h.
+ */
+
+ /**
+ * MetadataType represents the different types of buffer metadata that could be
+ * associated with a buffer. It is used by IMapper to help get and set buffer metadata
+ * on the buffer's native handle.
+ *
+ * Standard buffer metadata will have the name field set to
+ * "android.hardware.graphics.common.StandardMetadataType" and will contain values
+ * from StandardMetadataType.aidl.
+ *
+ * This struct should be "extended" by devices that use a proprietary or non-standard
+ * buffer metadata. To extend the struct, first create a custom @VendorStability vendor
+ * AIDL interface that defines the new type(s) you would like to support. Set the
+ * struct's name field to the custom aidl interface's name
+ * (eg. "vendor.mycompanyname.graphics.common.MetadataType"). Set the struct's value
+ * field to the custom @VendorStabilty vendor AIDL interface.
+ *
+ * Each company should create their own StandardMetadataType.aidl extension. The name
+ * field prevents values from different companies from colliding.
+ */
+ struct MetadataType {
+ string name;
+ int64_t value;
+ };
+
+ /**
+ * Gets the buffer metadata for a given MetadataType.
+ *
+ * Buffer metadata can be changed after allocation so clients should avoid "caching"
+ * the buffer metadata. For example, if the video resolution changes and the buffers
+ * are not reallocated, several buffer metadata values may change without warning.
+ * Clients should not expect the values to be constant. They should requery them every
+ * frame. The only exception is buffer metadata that is determined at allocation
+ * time. For StandardMetadataType values, only BUFFER_ID, NAME, WIDTH,
+ * HEIGHT, LAYER_COUNT, PIXEL_FORMAT_REQUESTED and USAGE are safe to cache because
+ * they are determined at allocation time.
+ *
+ * @param buffer Buffer containing desired metadata
+ * @param metadataType MetadataType for the metadata value being queried
+ * @return error Error status of the call, which may be
+ * - `NONE` upon success.
+ * - `BAD_BUFFER` if the raw handle is invalid.
+ * - `NO_RESOURCES` if the get cannot be fullfilled due to unavailability of
+ * resources.
+ * - `UNSUPPORTED` when metadataType is unknown/unsupported.
+ * IMapper must support getting all StandardMetadataType.aidl values defined
+ * at the time the device first launches.
+ * @return metadata Vector of bytes representing the buffer metadata associated with
+ * the MetadataType.
+ */
+ get(pointer buffer, MetadataType metadataType)
+ generates (Error error,
+ vec<uint8_t> metadata);
+
+ /**
+ * Sets the global value for a given MetadataType.
+ *
+ * Metadata fields are not required to be settable. This function can
+ * return Error::UNSUPPORTED whenever it doesn't support setting a
+ * particular Metadata field.
+ *
+ * The framework may attempt to set the following StandardMetadataType
+ * values: DATASPACE, PER_FRAME_METADATA, PER_FRAME_METADATA_BLOB and BLEND_MODE.
+ * We strongly encourage everyone to support setting as many of those fields as
+ * possible. If a device's Composer implementation supports a field, it should be
+ * supported here. Over time these metadata fields will be moved out of
+ * Composer/BufferQueue/etc. and into the buffer's Metadata fields.
+ * If a device's IMapper doesn't support setting those Metadata fields,
+ * eventually the device may not longer be able to support these fields.
+ *
+ * @param buffer Buffer receiving desired metadata
+ * @param metadataType MetadataType for the metadata value being set
+ * @param metadata Vector of bytes representing the value associated with
+ * @return error Error status of the call, which may be
+ * - `NONE` upon success.
+ * - `BAD_BUFFER` if the raw handle is invalid.
+ * - `BAD_VALUE` when the field is constant and can never be set (such as
+ * BUFFER_ID, NAME, WIDTH, HEIGHT, LAYER_COUNT, PIXEL_FORMAT_REQUESTED and
+ * USAGE)
+ * - `NO_RESOURCES` if the set cannot be fullfilled due to unavailability of
+ * resources.
+ * - `UNSUPPORTED` when metadataType is unknown/unsupported or setting
+ * it is unsupported. Unsupported should also be returned if the metadata
+ * is malformed.
+ */
+ set(pointer buffer, MetadataType metadataType, vec<uint8_t> metadata)
+ generates (Error error);
+
+ /**
+ * Given a BufferDescriptorInfo, gets the starting value of a given
+ * MetadataType. This can be used to query basic information about a buffer
+ * before the buffer is allocated.
+ *
+ * @param description Attributes of the descriptor.
+ * @param metadataType MetadataType for the metadata value being queried
+ * @return error Error status of the call, which may be
+ * - `NONE` upon success.
+ * - `BAD_VALUE` if any of the specified BufferDescriptorInfo attributes
+ * are invalid.
+ * - `NO_RESOURCES` if the get cannot be fullfilled due to unavailability of
+ * resources.
+ * - `UNSUPPORTED` when any of the description attributes are unsupported or
+ * if the metadataType is unknown/unsupported. This should also be
+ * returned if the requested metadata is not defined until a buffer has been
+ * allocated.
+ * @return metadata Vector of bytes representing the value associated with
+ * the MetadataType value.
+ */
+ getFromBufferDescriptorInfo(BufferDescriptorInfo description,
+ MetadataType metadataType)
+ generates (Error error,
+ vec<uint8_t> metadata);
+
+ struct MetadataTypeDescription {
+ MetadataType metadataType;
+ /**
+ * description should contain a string representation of the MetadataType.
+ *
+ * For example: "MyExampleMetadataType is a 64-bit timestamp in nanoseconds
+ * that indicates when a buffer is decoded. It is set by the media HAL after
+ * a buffer is decoded. It is used by the display HAL for hardware
+ * synchronization".
+ *
+ * This field is required for any non-StandardMetadataTypes.
+ */
+ string description;
+ /**
+ * isGettable represents if the MetadataType can be get.
+ */
+ bool isGettable;
+ /**
+ * isSettable represents if the MetadataType can be set.
+ */
+ bool isSettable;
+ };
+
+ /**
+ * Lists all the MetadataTypes supported by IMapper as well as a description
+ * of each supported MetadataType. For StandardMetadataTypes, the description
+ * string can be left empty.
+ *
+ * @return error Error status of the call, which may be
+ * - `NONE` upon success.
+ * - `NO_RESOURCES` if the get cannot be fullfilled due to unavailability of
+ * resources.
+ * @return descriptions Vector of MetadataTypeDescriptions that represent the
+ * MetadataTypes supported by the device.
+ */
+ listSupportedMetadataTypes()
+ generates (Error error, vec<MetadataTypeDescription> descriptions);
};
diff --git a/graphics/mapper/4.0/types.hal b/graphics/mapper/4.0/types.hal
index 2fdfa65..00b6607 100644
--- a/graphics/mapper/4.0/types.hal
+++ b/graphics/mapper/4.0/types.hal
@@ -53,32 +53,3 @@
*/
typedef vec<uint8_t> BufferDescriptor;
-/**
- * Structure for describing YCbCr formats for consumption by applications.
- * This is used with PixelFormat::YCBCR_*_888.
- *
- * Buffer chroma subsampling is defined in the format.
- * e.g. PixelFormat::YCBCR_420_888 has subsampling 4:2:0.
- *
- * Buffers must have a 8 bit depth.
- *
- * y, cb, and cr point to the first byte of their respective planes.
- *
- * Stride describes the distance in bytes from the first value of one row of
- * the image to the first value of the next row. It includes the width of the
- * image plus padding.
- * yStride is the stride of the luma plane.
- * cStride is the stride of the chroma planes.
- *
- * chromaStep is the distance in bytes from one chroma pixel value to the
- * next. This is 2 bytes for semiplanar (because chroma values are interleaved
- * and each chroma value is one byte) and 1 for planar.
- */
-struct YCbCrLayout {
- pointer y;
- pointer cb;
- pointer cr;
- uint32_t yStride;
- uint32_t cStride;
- uint32_t chromaStep;
-};
diff --git a/graphics/mapper/4.0/utils/vts/Android.bp b/graphics/mapper/4.0/utils/vts/Android.bp
index e451584..56ff116 100644
--- a/graphics/mapper/4.0/utils/vts/Android.bp
+++ b/graphics/mapper/4.0/utils/vts/Android.bp
@@ -26,6 +26,9 @@
"android.hardware.graphics.allocator@4.0",
"android.hardware.graphics.mapper@4.0",
],
+ shared_libs: [
+ "libgralloctypes",
+ ],
export_static_lib_headers: [
"android.hardware.graphics.allocator@4.0",
"android.hardware.graphics.mapper@4.0",
diff --git a/graphics/mapper/4.0/utils/vts/MapperVts.cpp b/graphics/mapper/4.0/utils/vts/MapperVts.cpp
index 056b7c9..8073e69 100644
--- a/graphics/mapper/4.0/utils/vts/MapperVts.cpp
+++ b/graphics/mapper/4.0/utils/vts/MapperVts.cpp
@@ -14,6 +14,7 @@
* limitations under the License.
*/
+#include <gralloctypes/Gralloc4.h>
#include <mapper-vts/4.0/MapperVts.h>
#include <VtsHalHidlTargetTestBase.h>
@@ -92,28 +93,39 @@
std::vector<const native_handle_t*> Gralloc::allocate(const BufferDescriptor& descriptor,
uint32_t count, bool import,
- uint32_t* outStride) {
+ bool allowFailure, uint32_t* outStride) {
std::vector<const native_handle_t*> bufferHandles;
bufferHandles.reserve(count);
- mAllocator->allocate(descriptor, count,
- [&](const auto& tmpError, const auto& tmpStride, const auto& tmpBuffers) {
- ASSERT_EQ(Error::NONE, tmpError) << "failed to allocate buffers";
- ASSERT_EQ(count, tmpBuffers.size()) << "invalid buffer array";
+ mAllocator->allocate(
+ descriptor, count,
+ [&](const auto& tmpError, const auto& tmpStride, const auto& tmpBuffers) {
+ ASSERT_EQ(Error::NONE, tmpError) << "failed to allocate buffers";
+ ASSERT_EQ(count, tmpBuffers.size()) << "invalid buffer array";
- for (uint32_t i = 0; i < count; i++) {
- if (import) {
- ASSERT_NO_FATAL_FAILURE(
- bufferHandles.push_back(importBuffer(tmpBuffers[i])));
- } else {
- ASSERT_NO_FATAL_FAILURE(
- bufferHandles.push_back(cloneBuffer(tmpBuffers[i])));
- }
- }
+ for (uint32_t i = 0; i < count; i++) {
+ const native_handle_t* bufferHandle = nullptr;
+ if (import) {
+ if (allowFailure) {
+ bufferHandle = importBuffer(tmpBuffers[i]);
+ } else {
+ ASSERT_NO_FATAL_FAILURE(bufferHandle = importBuffer(tmpBuffers[i]));
+ }
+ } else {
+ if (allowFailure) {
+ bufferHandle = cloneBuffer(tmpBuffers[i]);
+ } else {
+ ASSERT_NO_FATAL_FAILURE(bufferHandle = cloneBuffer(tmpBuffers[i]));
+ }
+ }
+ if (bufferHandle) {
+ bufferHandles.push_back(bufferHandle);
+ }
+ }
- if (outStride) {
- *outStride = tmpStride;
- }
- });
+ if (outStride) {
+ *outStride = tmpStride;
+ }
+ });
if (::testing::Test::HasFatalFailure()) {
bufferHandles.clear();
@@ -123,17 +135,20 @@
}
const native_handle_t* Gralloc::allocate(const IMapper::BufferDescriptorInfo& descriptorInfo,
- bool import, uint32_t* outStride) {
+ bool import, bool allowFailure, uint32_t* outStride) {
BufferDescriptor descriptor = createDescriptor(descriptorInfo);
if (::testing::Test::HasFatalFailure()) {
return nullptr;
}
- auto buffers = allocate(descriptor, 1, import, outStride);
+ auto buffers = allocate(descriptor, 1, import, allowFailure, outStride);
if (::testing::Test::HasFatalFailure()) {
return nullptr;
}
+ if (buffers.size() != 1) {
+ return nullptr;
+ }
return buffers[0];
}
@@ -167,6 +182,10 @@
}
void Gralloc::freeBuffer(const native_handle_t* bufferHandle) {
+ if (bufferHandle == nullptr) {
+ return;
+ }
+
auto buffer = const_cast<native_handle_t*>(bufferHandle);
if (mImportedBuffers.erase(bufferHandle)) {
@@ -180,8 +199,7 @@
}
void* Gralloc::lock(const native_handle_t* bufferHandle, uint64_t cpuUsage,
- const IMapper::Rect& accessRegion, int acquireFence, int32_t* outBytesPerPixel,
- int32_t* outBytesPerStride) {
+ const IMapper::Rect& accessRegion, int acquireFence) {
auto buffer = const_cast<native_handle_t*>(bufferHandle);
NATIVE_HANDLE_DECLARE_STORAGE(acquireFenceStorage, 1, 0);
@@ -192,17 +210,11 @@
acquireFenceHandle = h;
}
- *outBytesPerPixel = -1;
- *outBytesPerStride = -1;
-
void* data = nullptr;
mMapper->lock(buffer, cpuUsage, accessRegion, acquireFenceHandle,
- [&](const auto& tmpError, const auto& tmpData, int32_t tmpBytesPerPixel,
- int32_t tmpBytesPerStride) {
+ [&](const auto& tmpError, const auto& tmpData) {
ASSERT_EQ(Error::NONE, tmpError) << "failed to lock buffer " << buffer;
data = tmpData;
- *outBytesPerPixel = tmpBytesPerPixel;
- *outBytesPerStride = tmpBytesPerStride;
});
if (acquireFence >= 0) {
@@ -212,33 +224,6 @@
return data;
}
-YCbCrLayout Gralloc::lockYCbCr(const native_handle_t* bufferHandle, uint64_t cpuUsage,
- const IMapper::Rect& accessRegion, int acquireFence) {
- auto buffer = const_cast<native_handle_t*>(bufferHandle);
-
- NATIVE_HANDLE_DECLARE_STORAGE(acquireFenceStorage, 1, 0);
- hidl_handle acquireFenceHandle;
- if (acquireFence >= 0) {
- auto h = native_handle_init(acquireFenceStorage, 1, 0);
- h->data[0] = acquireFence;
- acquireFenceHandle = h;
- }
-
- YCbCrLayout layout = {};
- mMapper->lockYCbCr(buffer, cpuUsage, accessRegion, acquireFenceHandle,
- [&](const auto& tmpError, const auto& tmpLayout) {
- ASSERT_EQ(Error::NONE, tmpError)
- << "failed to lockYCbCr buffer " << buffer;
- layout = tmpLayout;
- });
-
- if (acquireFence >= 0) {
- close(acquireFence);
- }
-
- return layout;
-}
-
int Gralloc::unlock(const native_handle_t* bufferHandle) {
auto buffer = const_cast<native_handle_t*>(bufferHandle);
@@ -296,6 +281,35 @@
return supported;
}
+Error Gralloc::get(const native_handle_t* bufferHandle, const IMapper::MetadataType& metadataType,
+ hidl_vec<uint8_t>* outVec) {
+ Error err;
+ mMapper->get(const_cast<native_handle_t*>(bufferHandle), metadataType,
+ [&](const auto& tmpError, const hidl_vec<uint8_t>& tmpVec) {
+ err = tmpError;
+ *outVec = tmpVec;
+ });
+ return err;
+}
+
+Error Gralloc::set(const native_handle_t* bufferHandle, const IMapper::MetadataType& metadataType,
+ const hidl_vec<uint8_t>& vec) {
+ return mMapper->set(const_cast<native_handle_t*>(bufferHandle), metadataType, vec);
+}
+
+Error Gralloc::getFromBufferDescriptorInfo(const IMapper::BufferDescriptorInfo& descriptorInfo,
+ const IMapper::MetadataType& metadataType,
+ hidl_vec<uint8_t>* outVec) {
+ Error err;
+ mMapper->getFromBufferDescriptorInfo(
+ descriptorInfo, metadataType,
+ [&](const auto& tmpError, const hidl_vec<uint8_t>& tmpVec) {
+ err = tmpError;
+ *outVec = tmpVec;
+ });
+ return err;
+}
+
} // namespace vts
} // namespace V4_0
} // namespace mapper
diff --git a/graphics/mapper/4.0/utils/vts/include/mapper-vts/4.0/MapperVts.h b/graphics/mapper/4.0/utils/vts/include/mapper-vts/4.0/MapperVts.h
index 03ce764..6251e66 100644
--- a/graphics/mapper/4.0/utils/vts/include/mapper-vts/4.0/MapperVts.h
+++ b/graphics/mapper/4.0/utils/vts/include/mapper-vts/4.0/MapperVts.h
@@ -51,9 +51,11 @@
//
// Either case, the returned buffers must be freed with freeBuffer.
std::vector<const native_handle_t*> allocate(const BufferDescriptor& descriptor, uint32_t count,
- bool import = true, uint32_t* outStride = nullptr);
+ bool import = true, bool allowFailure = false,
+ uint32_t* outStride = nullptr);
const native_handle_t* allocate(const IMapper::BufferDescriptorInfo& descriptorInfo,
- bool import = true, uint32_t* outStride = nullptr);
+ bool import = true, bool allowFailure = false,
+ uint32_t* outStride = nullptr);
// IMapper methods
@@ -68,10 +70,7 @@
// in and out of the mapper. The ownership of the fd is always transferred
// with each of these functions.
void* lock(const native_handle_t* bufferHandle, uint64_t cpuUsage,
- const IMapper::Rect& accessRegion, int acquireFence, int32_t* outBytesPerPixel,
- int32_t* outBytesPerStride);
- YCbCrLayout lockYCbCr(const native_handle_t* bufferHandle, uint64_t cpuUsage,
- const IMapper::Rect& accessRegion, int acquireFence);
+ const IMapper::Rect& accessRegion, int acquireFence);
int unlock(const native_handle_t* bufferHandle);
bool validateBufferSize(const native_handle_t* bufferHandle,
@@ -81,6 +80,16 @@
bool isSupported(const IMapper::BufferDescriptorInfo& descriptorInfo);
+ Error get(const native_handle_t* bufferHandle, const IMapper::MetadataType& metadataType,
+ hidl_vec<uint8_t>* outVec);
+
+ Error set(const native_handle_t* bufferHandle, const IMapper::MetadataType& metadataType,
+ const hidl_vec<uint8_t>& vec);
+
+ Error getFromBufferDescriptorInfo(const IMapper::BufferDescriptorInfo& descriptorInfo,
+ const IMapper::MetadataType& metadataType,
+ hidl_vec<uint8_t>* outVec);
+
private:
void init(const std::string& allocatorServiceName, const std::string& mapperServiceName);
diff --git a/graphics/mapper/4.0/vts/functional/Android.bp b/graphics/mapper/4.0/vts/functional/Android.bp
index a90ee0c..5a7548a 100644
--- a/graphics/mapper/4.0/vts/functional/Android.bp
+++ b/graphics/mapper/4.0/vts/functional/Android.bp
@@ -19,12 +19,16 @@
defaults: ["VtsHalTargetTestDefaults"],
srcs: ["VtsHalGraphicsMapperV4_0TargetTest.cpp"],
static_libs: [
+ "android.hardware.graphics.mapper@4.0-vts",
+ ],
+ shared_libs: [
"android.hardware.graphics.allocator@4.0",
"android.hardware.graphics.common@1.0",
"android.hardware.graphics.common@1.1",
"android.hardware.graphics.common@1.2",
"android.hardware.graphics.mapper@4.0",
- "android.hardware.graphics.mapper@4.0-vts",
+ "libgralloctypes",
+ "vintf-graphics-common-ndk_platform",
],
test_suites: ["general-tests"],
}
diff --git a/graphics/mapper/4.0/vts/functional/VtsHalGraphicsMapperV4_0TargetTest.cpp b/graphics/mapper/4.0/vts/functional/VtsHalGraphicsMapperV4_0TargetTest.cpp
index 62ff613..dd748db 100644
--- a/graphics/mapper/4.0/vts/functional/VtsHalGraphicsMapperV4_0TargetTest.cpp
+++ b/graphics/mapper/4.0/vts/functional/VtsHalGraphicsMapperV4_0TargetTest.cpp
@@ -20,8 +20,12 @@
#include <thread>
#include <vector>
+//#include <aidl/android/hardware/graphics/common/BlendMode.h>
+//#include <aidl/android/hardware/graphics/common/Compression.h>
+
#include <VtsHalHidlTargetTestBase.h>
#include <android-base/logging.h>
+#include <gralloctypes/Gralloc4.h>
#include <mapper-vts/4.0/MapperVts.h>
namespace android {
@@ -34,6 +38,16 @@
using android::hardware::graphics::common::V1_2::BufferUsage;
using android::hardware::graphics::common::V1_2::PixelFormat;
+using MetadataType = android::hardware::graphics::mapper::V4_0::IMapper::MetadataType;
+using aidl::android::hardware::graphics::common::BlendMode;
+using aidl::android::hardware::graphics::common::Dataspace;
+using aidl::android::hardware::graphics::common::ExtendableType;
+using aidl::android::hardware::graphics::common::PlaneLayout;
+using aidl::android::hardware::graphics::common::PlaneLayoutComponent;
+using aidl::android::hardware::graphics::common::StandardMetadataType;
+
+using DecodeFunction = std::function<void(const IMapper::BufferDescriptorInfo& descriptorInfo,
+ const hidl_vec<uint8_t>& vec)>;
// Test environment for graphics.mapper.
class GraphicsMapperHidlEnvironment : public ::testing::VtsHalHidlTargetTestEnvBase {
@@ -57,6 +71,8 @@
mGralloc = std::make_unique<Gralloc>(
GraphicsMapperHidlEnvironment::Instance()->getServiceName<IAllocator>(),
GraphicsMapperHidlEnvironment::Instance()->getServiceName<IMapper>()));
+ ASSERT_NE(nullptr, mGralloc->getAllocator().get());
+ ASSERT_NE(nullptr, mGralloc->getMapper().get());
mDummyDescriptorInfo.name = "dummy";
mDummyDescriptorInfo.width = 64;
@@ -69,8 +85,108 @@
void TearDown() override {}
+ void testGet(const IMapper::BufferDescriptorInfo& descriptorInfo,
+ const MetadataType& metadataType, DecodeFunction decode) {
+ const native_handle_t* bufferHandle = nullptr;
+ ASSERT_NO_FATAL_FAILURE(bufferHandle = mGralloc->allocate(descriptorInfo, true));
+
+ hidl_vec<uint8_t> vec;
+ ASSERT_EQ(Error::NONE, mGralloc->get(bufferHandle, metadataType, &vec));
+
+ ASSERT_NO_FATAL_FAILURE(decode(descriptorInfo, vec));
+ }
+
+ void testSet(const IMapper::BufferDescriptorInfo& descriptorInfo,
+ const MetadataType& metadataType, const hidl_vec<uint8_t>& metadata,
+ DecodeFunction decode) {
+ const native_handle_t* bufferHandle = nullptr;
+ ASSERT_NO_FATAL_FAILURE(bufferHandle = mGralloc->allocate(descriptorInfo, true));
+
+ Error err = mGralloc->set(bufferHandle, metadataType, metadata);
+ if (err == Error::UNSUPPORTED) {
+ GTEST_SUCCEED() << "setting this metadata is unsupported";
+ }
+ ASSERT_EQ(err, Error::NONE);
+
+ hidl_vec<uint8_t> vec;
+ ASSERT_EQ(Error::NONE, mGralloc->get(bufferHandle, metadataType, &vec));
+
+ ASSERT_NO_FATAL_FAILURE(decode(descriptorInfo, vec));
+ }
+
+ void verifyDummyDescriptorInfoPlaneLayouts(const std::vector<PlaneLayout>& planeLayouts) {
+ ASSERT_EQ(1, planeLayouts.size());
+
+ const auto& planeLayout = planeLayouts.front();
+
+ ASSERT_EQ(4, planeLayout.components.size());
+
+ int64_t offsetInBitsR = -1;
+ int64_t offsetInBitsG = -1;
+ int64_t offsetInBitsB = -1;
+ int64_t offsetInBitsA = -1;
+
+ for (const auto& component : planeLayout.components) {
+ EXPECT_EQ(GRALLOC4_PLANE_LAYOUT_COMPONENT_TYPE, component.type.name);
+ EXPECT_EQ(8, component.sizeInBits);
+ if (component.type.value == gralloc4::PlaneLayoutComponentType_R.value) {
+ offsetInBitsR = component.offsetInBits;
+ }
+ if (component.type.value == gralloc4::PlaneLayoutComponentType_G.value) {
+ offsetInBitsG = component.offsetInBits;
+ }
+ if (component.type.value == gralloc4::PlaneLayoutComponentType_B.value) {
+ offsetInBitsB = component.offsetInBits;
+ }
+ if (component.type.value == gralloc4::PlaneLayoutComponentType_A.value) {
+ offsetInBitsA = component.offsetInBits;
+ }
+ }
+
+ EXPECT_EQ(0, offsetInBitsR);
+ EXPECT_EQ(8, offsetInBitsG);
+ EXPECT_EQ(16, offsetInBitsB);
+ EXPECT_EQ(24, offsetInBitsA);
+
+ EXPECT_EQ(0, planeLayout.offsetInBytes);
+ EXPECT_EQ(8, planeLayout.sampleIncrementInBits);
+ // Skip testing stride because any stride is valid
+ EXPECT_EQ(mDummyDescriptorInfo.width, planeLayout.widthInSamples);
+ EXPECT_EQ(mDummyDescriptorInfo.height, planeLayout.heightInSamples);
+ EXPECT_LE(planeLayout.widthInSamples * planeLayout.heightInSamples * 4,
+ planeLayout.totalSizeInBytes);
+ EXPECT_EQ(1, planeLayout.horizontalSubsampling);
+ EXPECT_EQ(1, planeLayout.verticalSubsampling);
+
+ EXPECT_EQ(0, planeLayout.crop.left);
+ EXPECT_EQ(0, planeLayout.crop.top);
+ EXPECT_EQ(planeLayout.widthInSamples, planeLayout.crop.right);
+ EXPECT_EQ(planeLayout.heightInSamples, planeLayout.crop.bottom);
+ }
+
std::unique_ptr<Gralloc> mGralloc;
IMapper::BufferDescriptorInfo mDummyDescriptorInfo{};
+ static const std::set<StandardMetadataType> sRequiredMetadataTypes;
+};
+
+const std::set<StandardMetadataType> GraphicsMapperHidlTest::sRequiredMetadataTypes{
+ StandardMetadataType::BUFFER_ID,
+ StandardMetadataType::NAME,
+ StandardMetadataType::WIDTH,
+ StandardMetadataType::HEIGHT,
+ StandardMetadataType::LAYER_COUNT,
+ StandardMetadataType::PIXEL_FORMAT_REQUESTED,
+ StandardMetadataType::PIXEL_FORMAT_FOURCC,
+ StandardMetadataType::PIXEL_FORMAT_MODIFIER,
+ StandardMetadataType::USAGE,
+ StandardMetadataType::ALLOCATION_SIZE,
+ StandardMetadataType::PROTECTED_CONTENT,
+ StandardMetadataType::COMPRESSION,
+ StandardMetadataType::INTERLACED,
+ StandardMetadataType::CHROMA_SITING,
+ StandardMetadataType::PLANE_LAYOUTS,
+ StandardMetadataType::DATASPACE,
+ StandardMetadataType::BLEND_MODE,
};
/**
@@ -90,8 +206,8 @@
for (uint32_t count = 0; count < 5; count++) {
std::vector<const native_handle_t*> bufferHandles;
uint32_t stride;
- ASSERT_NO_FATAL_FAILURE(bufferHandles =
- mGralloc->allocate(descriptor, count, false, &stride));
+ ASSERT_NO_FATAL_FAILURE(
+ bufferHandles = mGralloc->allocate(descriptor, count, false, false, &stride));
if (count >= 1) {
EXPECT_LE(mDummyDescriptorInfo.width, stride) << "invalid buffer stride";
@@ -293,22 +409,15 @@
const native_handle_t* bufferHandle;
uint32_t stride;
- ASSERT_NO_FATAL_FAILURE(bufferHandle = mGralloc->allocate(info, true, &stride));
+ ASSERT_NO_FATAL_FAILURE(bufferHandle = mGralloc->allocate(info, true, false, &stride));
// lock buffer for writing
const IMapper::Rect region{0, 0, static_cast<int32_t>(info.width),
static_cast<int32_t>(info.height)};
int fence = -1;
uint8_t* data;
- int32_t bytesPerPixel = -1;
- int32_t bytesPerStride = -1;
ASSERT_NO_FATAL_FAILURE(
- data = static_cast<uint8_t*>(mGralloc->lock(bufferHandle, info.usage, region, fence,
- &bytesPerPixel, &bytesPerStride)));
-
- // Valid return values are -1 for unsupported or the number bytes for supported which is >=0
- EXPECT_GT(bytesPerPixel, -1);
- EXPECT_GT(bytesPerStride, -1);
+ data = static_cast<uint8_t*>(mGralloc->lock(bufferHandle, info.usage, region, fence)));
// RGBA_8888
size_t strideInBytes = stride * 4;
@@ -321,13 +430,9 @@
ASSERT_NO_FATAL_FAILURE(fence = mGralloc->unlock(bufferHandle));
- bytesPerPixel = -1;
- bytesPerStride = -1;
-
// lock again for reading
ASSERT_NO_FATAL_FAILURE(
- data = static_cast<uint8_t*>(mGralloc->lock(bufferHandle, info.usage, region, fence,
- &bytesPerPixel, &bytesPerStride)));
+ data = static_cast<uint8_t*>(mGralloc->lock(bufferHandle, info.usage, region, fence)));
for (uint32_t y = 0; y < info.height; y++) {
for (size_t i = 0; i < writeInBytes; i++) {
EXPECT_EQ(static_cast<uint8_t>(y), data[i]);
@@ -335,69 +440,6 @@
data += strideInBytes;
}
- EXPECT_GT(bytesPerPixel, -1);
- EXPECT_GT(bytesPerStride, -1);
-
- ASSERT_NO_FATAL_FAILURE(fence = mGralloc->unlock(bufferHandle));
- if (fence >= 0) {
- close(fence);
- }
-}
-
-/**
- * Test IMapper::lockYCbCr. This locks a YV12 buffer, and makes sure we can
- * write to and read from it.
- */
-TEST_F(GraphicsMapperHidlTest, LockYCbCrBasic) {
- auto info = mDummyDescriptorInfo;
- info.format = PixelFormat::YV12;
-
- const native_handle_t* bufferHandle;
- uint32_t stride;
- ASSERT_NO_FATAL_FAILURE(bufferHandle = mGralloc->allocate(info, true, &stride));
-
- // lock buffer for writing
- const IMapper::Rect region{0, 0, static_cast<int32_t>(info.width),
- static_cast<int32_t>(info.height)};
- int fence = -1;
- YCbCrLayout layout;
- ASSERT_NO_FATAL_FAILURE(layout = mGralloc->lockYCbCr(bufferHandle, info.usage, region, fence));
-
- auto yData = static_cast<uint8_t*>(layout.y);
- auto cbData = static_cast<uint8_t*>(layout.cb);
- auto crData = static_cast<uint8_t*>(layout.cr);
- for (uint32_t y = 0; y < info.height; y++) {
- for (uint32_t x = 0; x < info.width; x++) {
- auto val = static_cast<uint8_t>(info.height * y + x);
-
- yData[layout.yStride * y + x] = val;
- if (y % 2 == 0 && x % 2 == 0) {
- cbData[layout.cStride * y / 2 + x / 2] = val;
- crData[layout.cStride * y / 2 + x / 2] = val;
- }
- }
- }
-
- ASSERT_NO_FATAL_FAILURE(fence = mGralloc->unlock(bufferHandle));
-
- // lock again for reading
- ASSERT_NO_FATAL_FAILURE(layout = mGralloc->lockYCbCr(bufferHandle, info.usage, region, fence));
-
- yData = static_cast<uint8_t*>(layout.y);
- cbData = static_cast<uint8_t*>(layout.cb);
- crData = static_cast<uint8_t*>(layout.cr);
- for (uint32_t y = 0; y < info.height; y++) {
- for (uint32_t x = 0; x < info.width; x++) {
- auto val = static_cast<uint8_t>(info.height * y + x);
-
- EXPECT_EQ(val, yData[layout.yStride * y + x]);
- if (y % 2 == 0 && x % 2 == 0) {
- EXPECT_EQ(val, cbData[layout.cStride * y / 2 + x / 2]);
- EXPECT_EQ(val, crData[layout.cStride * y / 2 + x / 2]);
- }
- }
- }
-
ASSERT_NO_FATAL_FAILURE(fence = mGralloc->unlock(bufferHandle));
if (fence >= 0) {
close(fence);
@@ -427,8 +469,7 @@
auto buffer = const_cast<native_handle_t*>(bufferHandle);
mGralloc->getMapper()->lock(buffer, info.usage, accessRegion, acquireFenceHandle,
- [&](const auto& tmpError, const auto& /*tmpData*/,
- int32_t /*tmpBytesPerPixel*/, int32_t /*tmpBytesPerStride*/) {
+ [&](const auto& tmpError, const auto& /*tmpData*/) {
EXPECT_EQ(Error::BAD_VALUE, tmpError)
<< "locking with a bad access region should fail";
});
@@ -519,6 +560,1035 @@
ASSERT_NO_FATAL_FAILURE(supported = mGralloc->isSupported(info));
}
+/**
+ * Test IMapper::get(BufferId)
+ */
+TEST_F(GraphicsMapperHidlTest, GetBufferId) {
+ testGet(mDummyDescriptorInfo, gralloc4::MetadataType_BufferId,
+ [](const IMapper::BufferDescriptorInfo& /*info*/, const hidl_vec<uint8_t>& vec) {
+ uint64_t bufferId = 0;
+ ASSERT_EQ(NO_ERROR, gralloc4::decodeBufferId(vec, &bufferId));
+ });
+}
+
+/**
+ * Test IMapper::get(Name)
+ */
+TEST_F(GraphicsMapperHidlTest, GetName) {
+ testGet(mDummyDescriptorInfo, gralloc4::MetadataType_Name,
+ [](const IMapper::BufferDescriptorInfo& info, const hidl_vec<uint8_t>& vec) {
+ std::string name;
+ ASSERT_EQ(NO_ERROR, gralloc4::decodeName(vec, &name));
+ EXPECT_EQ(info.name, name);
+ });
+}
+
+/**
+ * Test IMapper::get(Width)
+ */
+TEST_F(GraphicsMapperHidlTest, GetWidth) {
+ testGet(mDummyDescriptorInfo, gralloc4::MetadataType_Width,
+ [](const IMapper::BufferDescriptorInfo& info, const hidl_vec<uint8_t>& vec) {
+ uint64_t width = 0;
+ ASSERT_EQ(NO_ERROR, gralloc4::decodeWidth(vec, &width));
+ EXPECT_EQ(info.width, width);
+ });
+}
+
+/**
+ * Test IMapper::get(Height)
+ */
+TEST_F(GraphicsMapperHidlTest, GetHeight) {
+ testGet(mDummyDescriptorInfo, gralloc4::MetadataType_Height,
+ [](const IMapper::BufferDescriptorInfo& info, const hidl_vec<uint8_t>& vec) {
+ uint64_t height = 0;
+ ASSERT_EQ(NO_ERROR, gralloc4::decodeHeight(vec, &height));
+ EXPECT_EQ(info.height, height);
+ });
+}
+
+/**
+ * Test IMapper::get(LayerCount)
+ */
+TEST_F(GraphicsMapperHidlTest, GetLayerCount) {
+ testGet(mDummyDescriptorInfo, gralloc4::MetadataType_LayerCount,
+ [](const IMapper::BufferDescriptorInfo& info, const hidl_vec<uint8_t>& vec) {
+ uint64_t layerCount = 0;
+ ASSERT_EQ(NO_ERROR, gralloc4::decodeLayerCount(vec, &layerCount));
+ EXPECT_EQ(info.layerCount, layerCount);
+ });
+}
+
+/**
+ * Test IMapper::get(PixelFormatRequested)
+ */
+TEST_F(GraphicsMapperHidlTest, GetPixelFormatRequested) {
+ testGet(mDummyDescriptorInfo, gralloc4::MetadataType_PixelFormatRequested,
+ [](const IMapper::BufferDescriptorInfo& info, const hidl_vec<uint8_t>& vec) {
+ PixelFormat pixelFormatRequested = PixelFormat::BLOB;
+ ASSERT_EQ(NO_ERROR,
+ gralloc4::decodePixelFormatRequested(vec, &pixelFormatRequested));
+ EXPECT_EQ(info.format, pixelFormatRequested);
+ });
+}
+
+/**
+ * Test IMapper::get(PixelFormatFourCC)
+ */
+TEST_F(GraphicsMapperHidlTest, GetPixelFormatFourCC) {
+ testGet(mDummyDescriptorInfo, gralloc4::MetadataType_PixelFormatFourCC,
+ [](const IMapper::BufferDescriptorInfo& /*info*/, const hidl_vec<uint8_t>& vec) {
+ uint32_t pixelFormatFourCC = 0;
+ ASSERT_EQ(NO_ERROR, gralloc4::decodePixelFormatFourCC(vec, &pixelFormatFourCC));
+ });
+}
+
+/**
+ * Test IMapper::get(PixelFormatModifier)
+ */
+TEST_F(GraphicsMapperHidlTest, GetPixelFormatModifier) {
+ testGet(mDummyDescriptorInfo, gralloc4::MetadataType_PixelFormatModifier,
+ [](const IMapper::BufferDescriptorInfo& /*info*/, const hidl_vec<uint8_t>& vec) {
+ uint64_t pixelFormatModifier = 0;
+ ASSERT_EQ(NO_ERROR, gralloc4::decodePixelFormatModifier(vec, &pixelFormatModifier));
+ });
+}
+
+/**
+ * Test IMapper::get(Usage)
+ */
+TEST_F(GraphicsMapperHidlTest, GetUsage) {
+ testGet(mDummyDescriptorInfo, gralloc4::MetadataType_Usage,
+ [](const IMapper::BufferDescriptorInfo& info, const hidl_vec<uint8_t>& vec) {
+ uint64_t usage = 0;
+ ASSERT_EQ(NO_ERROR, gralloc4::decodeUsage(vec, &usage));
+ EXPECT_EQ(info.usage, usage);
+ });
+}
+
+/**
+ * Test IMapper::get(AllocationSize)
+ */
+TEST_F(GraphicsMapperHidlTest, GetAllocationSize) {
+ testGet(mDummyDescriptorInfo, gralloc4::MetadataType_AllocationSize,
+ [](const IMapper::BufferDescriptorInfo& /*info*/, const hidl_vec<uint8_t>& vec) {
+ uint64_t allocationSize = 0;
+ ASSERT_EQ(NO_ERROR, gralloc4::decodeAllocationSize(vec, &allocationSize));
+ });
+}
+
+/**
+ * Test IMapper::get(ProtectedContent)
+ */
+TEST_F(GraphicsMapperHidlTest, GetProtectedContent) {
+ auto info = mDummyDescriptorInfo;
+ info.usage = BufferUsage::PROTECTED | BufferUsage::COMPOSER_OVERLAY;
+
+ const native_handle_t* bufferHandle = nullptr;
+ bufferHandle = mGralloc->allocate(info, true, true);
+ if (bufferHandle) {
+ GTEST_SUCCEED() << "unable to allocate protected content";
+ }
+
+ hidl_vec<uint8_t> vec;
+ ASSERT_EQ(Error::NONE,
+ mGralloc->get(bufferHandle, gralloc4::MetadataType_ProtectedContent, &vec));
+
+ uint64_t protectedContent = 0;
+ ASSERT_EQ(NO_ERROR, gralloc4::decodeProtectedContent(vec, &protectedContent));
+ EXPECT_EQ(1, protectedContent);
+}
+
+/**
+ * Test IMapper::get(Compression)
+ */
+TEST_F(GraphicsMapperHidlTest, GetCompression) {
+ auto info = mDummyDescriptorInfo;
+ info.usage = static_cast<uint64_t>(BufferUsage::CPU_WRITE_OFTEN | BufferUsage::CPU_READ_OFTEN);
+
+ testGet(info, gralloc4::MetadataType_Compression,
+ [](const IMapper::BufferDescriptorInfo& /*info*/, const hidl_vec<uint8_t>& vec) {
+ ExtendableType compression = gralloc4::Compression_DisplayStreamCompression;
+ ASSERT_EQ(NO_ERROR, gralloc4::decodeCompression(vec, &compression));
+
+ EXPECT_EQ(gralloc4::Compression_None.name, compression.name);
+ EXPECT_EQ(gralloc4::Compression_None.value, compression.value);
+ });
+}
+
+/**
+ * Test IMapper::get(Interlaced)
+ */
+TEST_F(GraphicsMapperHidlTest, GetInterlaced) {
+ testGet(mDummyDescriptorInfo, gralloc4::MetadataType_Interlaced,
+ [](const IMapper::BufferDescriptorInfo& /*info*/, const hidl_vec<uint8_t>& vec) {
+ ExtendableType interlaced = gralloc4::Interlaced_TopBottom;
+ ASSERT_EQ(NO_ERROR, gralloc4::decodeInterlaced(vec, &interlaced));
+
+ EXPECT_EQ(gralloc4::Interlaced_None.name, interlaced.name);
+ EXPECT_EQ(gralloc4::Interlaced_None.value, interlaced.value);
+ });
+}
+
+/**
+ * Test IMapper::get(ChromaSiting)
+ */
+TEST_F(GraphicsMapperHidlTest, GetChromaSiting) {
+ testGet(mDummyDescriptorInfo, gralloc4::MetadataType_ChromaSiting,
+ [](const IMapper::BufferDescriptorInfo& /*info*/, const hidl_vec<uint8_t>& vec) {
+ ExtendableType chromaSiting = gralloc4::ChromaSiting_Unknown;
+ ASSERT_EQ(NO_ERROR, gralloc4::decodeChromaSiting(vec, &chromaSiting));
+
+ EXPECT_EQ(gralloc4::ChromaSiting_None.name, chromaSiting.name);
+ EXPECT_EQ(gralloc4::ChromaSiting_None.value, chromaSiting.value);
+ });
+}
+
+/**
+ * Test IMapper::get(PlaneLayouts)
+ */
+TEST_F(GraphicsMapperHidlTest, GetPlaneLayouts) {
+ const native_handle_t* bufferHandle = nullptr;
+ ASSERT_NO_FATAL_FAILURE(bufferHandle = mGralloc->allocate(mDummyDescriptorInfo, true));
+
+ hidl_vec<uint8_t> vec;
+ ASSERT_EQ(Error::NONE, mGralloc->get(bufferHandle, gralloc4::MetadataType_PlaneLayouts, &vec));
+
+ std::vector<PlaneLayout> planeLayouts;
+ ASSERT_EQ(NO_ERROR, gralloc4::decodePlaneLayouts(vec, &planeLayouts));
+
+ ASSERT_NO_FATAL_FAILURE(verifyDummyDescriptorInfoPlaneLayouts(planeLayouts));
+}
+
+/**
+ * Test IMapper::get(Dataspace)
+ */
+TEST_F(GraphicsMapperHidlTest, GetDataspace) {
+ testGet(mDummyDescriptorInfo, gralloc4::MetadataType_Dataspace,
+ [](const IMapper::BufferDescriptorInfo& /*info*/, const hidl_vec<uint8_t>& vec) {
+ Dataspace dataspace = Dataspace::DISPLAY_P3;
+ ASSERT_EQ(NO_ERROR, gralloc4::decodeDataspace(vec, &dataspace));
+ EXPECT_EQ(Dataspace::UNKNOWN, dataspace);
+ });
+}
+
+/**
+ * Test IMapper::get(BlendMode)
+ */
+TEST_F(GraphicsMapperHidlTest, GetBlendMode) {
+ testGet(mDummyDescriptorInfo, gralloc4::MetadataType_BlendMode,
+ [](const IMapper::BufferDescriptorInfo& /*info*/, const hidl_vec<uint8_t>& vec) {
+ BlendMode blendMode = BlendMode::NONE;
+ ASSERT_EQ(NO_ERROR, gralloc4::decodeBlendMode(vec, &blendMode));
+ EXPECT_EQ(BlendMode::INVALID, blendMode);
+ });
+}
+
+/**
+ * Test IMapper::get(metadata) with a bad buffer
+ */
+TEST_F(GraphicsMapperHidlTest, GetMetadataBadValue) {
+ const native_handle_t* bufferHandle = nullptr;
+ hidl_vec<uint8_t> vec;
+ ASSERT_EQ(Error::BAD_BUFFER,
+ mGralloc->get(bufferHandle, gralloc4::MetadataType_BufferId, &vec));
+ ASSERT_EQ(0, vec.size());
+ ASSERT_EQ(Error::BAD_BUFFER, mGralloc->get(bufferHandle, gralloc4::MetadataType_Name, &vec));
+ ASSERT_EQ(0, vec.size());
+ ASSERT_EQ(Error::BAD_BUFFER, mGralloc->get(bufferHandle, gralloc4::MetadataType_Width, &vec));
+ ASSERT_EQ(0, vec.size());
+ ASSERT_EQ(Error::BAD_BUFFER, mGralloc->get(bufferHandle, gralloc4::MetadataType_Height, &vec));
+ ASSERT_EQ(0, vec.size());
+ ASSERT_EQ(Error::BAD_BUFFER,
+ mGralloc->get(bufferHandle, gralloc4::MetadataType_LayerCount, &vec));
+ ASSERT_EQ(0, vec.size());
+ ASSERT_EQ(Error::BAD_BUFFER,
+ mGralloc->get(bufferHandle, gralloc4::MetadataType_PixelFormatRequested, &vec));
+ ASSERT_EQ(0, vec.size());
+ ASSERT_EQ(Error::BAD_BUFFER,
+ mGralloc->get(bufferHandle, gralloc4::MetadataType_PixelFormatFourCC, &vec));
+ ASSERT_EQ(0, vec.size());
+ ASSERT_EQ(Error::BAD_BUFFER,
+ mGralloc->get(bufferHandle, gralloc4::MetadataType_PixelFormatModifier, &vec));
+ ASSERT_EQ(0, vec.size());
+ ASSERT_EQ(Error::BAD_BUFFER, mGralloc->get(bufferHandle, gralloc4::MetadataType_Usage, &vec));
+ ASSERT_EQ(0, vec.size());
+ ASSERT_EQ(Error::BAD_BUFFER,
+ mGralloc->get(bufferHandle, gralloc4::MetadataType_AllocationSize, &vec));
+ ASSERT_EQ(0, vec.size());
+ ASSERT_EQ(Error::BAD_BUFFER,
+ mGralloc->get(bufferHandle, gralloc4::MetadataType_ProtectedContent, &vec));
+ ASSERT_EQ(0, vec.size());
+ ASSERT_EQ(Error::BAD_BUFFER,
+ mGralloc->get(bufferHandle, gralloc4::MetadataType_Compression, &vec));
+ ASSERT_EQ(0, vec.size());
+ ASSERT_EQ(Error::BAD_BUFFER,
+ mGralloc->get(bufferHandle, gralloc4::MetadataType_Interlaced, &vec));
+ ASSERT_EQ(0, vec.size());
+ ASSERT_EQ(Error::BAD_BUFFER,
+ mGralloc->get(bufferHandle, gralloc4::MetadataType_ChromaSiting, &vec));
+ ASSERT_EQ(0, vec.size());
+ ASSERT_EQ(Error::BAD_BUFFER,
+ mGralloc->get(bufferHandle, gralloc4::MetadataType_PlaneLayouts, &vec));
+ ASSERT_EQ(0, vec.size());
+ ASSERT_EQ(Error::BAD_BUFFER,
+ mGralloc->get(bufferHandle, gralloc4::MetadataType_Dataspace, &vec));
+ ASSERT_EQ(0, vec.size());
+ ASSERT_EQ(Error::BAD_BUFFER,
+ mGralloc->get(bufferHandle, gralloc4::MetadataType_BlendMode, &vec));
+ ASSERT_EQ(0, vec.size());
+}
+
+/**
+ * Test IMapper::get(metadata) for unsupported metadata
+ */
+TEST_F(GraphicsMapperHidlTest, GetUnsupportedMetadata) {
+ const native_handle_t* bufferHandle = nullptr;
+ ASSERT_NO_FATAL_FAILURE(bufferHandle = mGralloc->allocate(mDummyDescriptorInfo, true));
+
+ MetadataType metadataTypeFake = {"FAKE", 1};
+
+ hidl_vec<uint8_t> vec;
+ ASSERT_EQ(Error::UNSUPPORTED, mGralloc->get(bufferHandle, metadataTypeFake, &vec));
+ ASSERT_EQ(0, vec.size());
+}
+
+/**
+ * Test IMapper::get(metadata) for unsupported standard metadata
+ */
+TEST_F(GraphicsMapperHidlTest, GetUnsupportedStandardMetadata) {
+ const native_handle_t* bufferHandle = nullptr;
+ ASSERT_NO_FATAL_FAILURE(bufferHandle = mGralloc->allocate(mDummyDescriptorInfo, true));
+
+ MetadataType metadataTypeFake = {GRALLOC4_STANDARD_METADATA_TYPE, 9999};
+
+ hidl_vec<uint8_t> vec;
+ ASSERT_EQ(Error::UNSUPPORTED, mGralloc->get(bufferHandle, metadataTypeFake, &vec));
+ ASSERT_EQ(0, vec.size());
+}
+
+/**
+ * Test IMapper::set(PixelFormatFourCC)
+ */
+TEST_F(GraphicsMapperHidlTest, SetPixelFormatFourCC) {
+ uint32_t pixelFormatFourCC = 0x34324142; // DRM_FORMAT_BGRA8888
+ hidl_vec<uint8_t> vec;
+ ASSERT_EQ(NO_ERROR, gralloc4::encodePixelFormatFourCC(pixelFormatFourCC, &vec));
+
+ testSet(mDummyDescriptorInfo, gralloc4::MetadataType_PixelFormatFourCC, vec,
+ [&](const IMapper::BufferDescriptorInfo& /*info*/, const hidl_vec<uint8_t>& vec) {
+ uint32_t realPixelFormatFourCC = 0;
+ ASSERT_EQ(NO_ERROR, gralloc4::decodePixelFormatFourCC(vec, &realPixelFormatFourCC));
+ EXPECT_EQ(pixelFormatFourCC, realPixelFormatFourCC);
+ });
+}
+
+/**
+ * Test IMapper::set(PixelFormatModifier)
+ */
+TEST_F(GraphicsMapperHidlTest, SetPixelFormatModifier) {
+ uint64_t pixelFormatModifier = 10;
+ hidl_vec<uint8_t> vec;
+ ASSERT_EQ(NO_ERROR, gralloc4::encodePixelFormatModifier(pixelFormatModifier, &vec));
+
+ testSet(mDummyDescriptorInfo, gralloc4::MetadataType_PixelFormatModifier, vec,
+ [&](const IMapper::BufferDescriptorInfo& /*info*/, const hidl_vec<uint8_t>& vec) {
+ uint64_t realPixelFormatModifier = 0;
+ ASSERT_EQ(NO_ERROR,
+ gralloc4::decodePixelFormatModifier(vec, &realPixelFormatModifier));
+ EXPECT_EQ(pixelFormatModifier, realPixelFormatModifier);
+ });
+}
+
+/**
+ * Test IMapper::set(Usage) remove flag
+ */
+TEST_F(GraphicsMapperHidlTest, SetUsageRemoveBit) {
+ uint64_t usage = static_cast<uint64_t>(BufferUsage::CPU_WRITE_OFTEN);
+ hidl_vec<uint8_t> vec;
+ ASSERT_EQ(NO_ERROR, gralloc4::encodeUsage(usage, &vec));
+
+ testSet(mDummyDescriptorInfo, gralloc4::MetadataType_Usage, vec,
+ [&](const IMapper::BufferDescriptorInfo& /*info*/, const hidl_vec<uint8_t>& vec) {
+ uint64_t realUsage = 0;
+ ASSERT_EQ(NO_ERROR, gralloc4::decodeUsage(vec, &realUsage));
+ EXPECT_EQ(usage, realUsage);
+ });
+}
+/**
+ * Test IMapper::set(Usage) add flag
+ */
+TEST_F(GraphicsMapperHidlTest, SetUsageAddBit) {
+ uint64_t usage = mDummyDescriptorInfo.usage | static_cast<uint64_t>(BufferUsage::GPU_TEXTURE);
+ hidl_vec<uint8_t> vec;
+ ASSERT_EQ(NO_ERROR, gralloc4::encodeUsage(usage, &vec));
+
+ testSet(mDummyDescriptorInfo, gralloc4::MetadataType_Usage, vec,
+ [&](const IMapper::BufferDescriptorInfo& /*info*/, const hidl_vec<uint8_t>& vec) {
+ uint64_t realUsage = 0;
+ ASSERT_EQ(NO_ERROR, gralloc4::decodeUsage(vec, &realUsage));
+ EXPECT_EQ(usage, realUsage);
+ });
+}
+
+/**
+ * Test IMapper::set(Usage) to test protected content
+ */
+TEST_F(GraphicsMapperHidlTest, SetUsageProtected) {
+ const native_handle_t* bufferHandle = nullptr;
+ auto info = mDummyDescriptorInfo;
+ info.usage = BufferUsage::PROTECTED | BufferUsage::COMPOSER_OVERLAY;
+
+ bufferHandle = mGralloc->allocate(info, true, true);
+ if (bufferHandle) {
+ GTEST_SUCCEED() << "unable to allocate protected content";
+ }
+
+ uint64_t usage = static_cast<uint64_t>(BufferUsage::COMPOSER_OVERLAY);
+ hidl_vec<uint8_t> vec;
+ ASSERT_EQ(NO_ERROR, gralloc4::encodeUsage(usage, &vec));
+
+ Error err = mGralloc->set(bufferHandle, gralloc4::MetadataType_Usage, vec);
+ ASSERT_EQ(err, Error::UNSUPPORTED);
+ vec.resize(0);
+
+ uint64_t realUsage = 0;
+ ASSERT_EQ(Error::NONE, mGralloc->get(bufferHandle, gralloc4::MetadataType_Usage, &vec));
+ ASSERT_EQ(NO_ERROR, gralloc4::decodeUsage(vec, &realUsage));
+ EXPECT_EQ(info.usage, realUsage);
+}
+
+/**
+ * Test IMapper::set(AllocationSize)
+ */
+TEST_F(GraphicsMapperHidlTest, SetAllocationSize) {
+ uint64_t allocationSize = 1000000;
+ hidl_vec<uint8_t> vec;
+ ASSERT_EQ(NO_ERROR, gralloc4::encodeAllocationSize(allocationSize, &vec));
+
+ testSet(mDummyDescriptorInfo, gralloc4::MetadataType_AllocationSize, vec,
+ [&](const IMapper::BufferDescriptorInfo& /*info*/, const hidl_vec<uint8_t>& vec) {
+ uint64_t realAllocationSize = 0;
+ ASSERT_EQ(NO_ERROR, gralloc4::decodeAllocationSize(vec, &realAllocationSize));
+ EXPECT_EQ(allocationSize, realAllocationSize);
+ });
+}
+
+/**
+ * Test IMapper::set(ProtectedContent)
+ */
+TEST_F(GraphicsMapperHidlTest, SetProtectedContent) {
+ const native_handle_t* bufferHandle = nullptr;
+ auto info = mDummyDescriptorInfo;
+ info.usage = BufferUsage::PROTECTED | BufferUsage::COMPOSER_OVERLAY;
+
+ bufferHandle = mGralloc->allocate(info, true, true);
+ if (bufferHandle) {
+ GTEST_SUCCEED() << "unable to allocate protected content";
+ }
+
+ uint64_t protectedContent = 0;
+ hidl_vec<uint8_t> vec;
+ ASSERT_EQ(NO_ERROR, gralloc4::encodeProtectedContent(protectedContent, &vec));
+
+ Error err = mGralloc->set(bufferHandle, gralloc4::MetadataType_ProtectedContent, vec);
+ ASSERT_EQ(err, Error::UNSUPPORTED);
+ vec.resize(0);
+
+ uint64_t realProtectedContent = 0;
+ ASSERT_EQ(Error::NONE,
+ mGralloc->get(bufferHandle, gralloc4::MetadataType_ProtectedContent, &vec));
+ ASSERT_EQ(NO_ERROR, gralloc4::decodeProtectedContent(vec, &realProtectedContent));
+ EXPECT_EQ(1, realProtectedContent);
+}
+
+/**
+ * Test IMapper::set(Compression)
+ */
+TEST_F(GraphicsMapperHidlTest, SetCompression) {
+ auto info = mDummyDescriptorInfo;
+ info.usage = static_cast<uint64_t>(BufferUsage::CPU_WRITE_OFTEN | BufferUsage::CPU_READ_OFTEN);
+
+ ExtendableType compression = gralloc4::Compression_DisplayStreamCompression;
+ hidl_vec<uint8_t> vec;
+ ASSERT_EQ(NO_ERROR, gralloc4::encodeCompression(compression, &vec));
+
+ testSet(info, gralloc4::MetadataType_Compression, vec,
+ [&](const IMapper::BufferDescriptorInfo& /*info*/, const hidl_vec<uint8_t>& vec) {
+ ExtendableType realCompression = gralloc4::Compression_None;
+ ASSERT_EQ(NO_ERROR, gralloc4::decodeCompression(vec, &realCompression));
+
+ EXPECT_EQ(compression.name, realCompression.name);
+ EXPECT_EQ(compression.value, realCompression.value);
+ });
+}
+
+/**
+ * Test IMapper::set(Interlaced)
+ */
+TEST_F(GraphicsMapperHidlTest, SetInterlaced) {
+ ExtendableType interlaced = gralloc4::Interlaced_RightLeft;
+ hidl_vec<uint8_t> vec;
+ ASSERT_EQ(NO_ERROR, gralloc4::encodeInterlaced(interlaced, &vec));
+
+ testSet(mDummyDescriptorInfo, gralloc4::MetadataType_Interlaced, vec,
+ [&](const IMapper::BufferDescriptorInfo& /*info*/, const hidl_vec<uint8_t>& vec) {
+ ExtendableType realInterlaced = gralloc4::Interlaced_None;
+ ASSERT_EQ(NO_ERROR, gralloc4::decodeInterlaced(vec, &realInterlaced));
+
+ EXPECT_EQ(interlaced.name, realInterlaced.name);
+ EXPECT_EQ(interlaced.value, realInterlaced.value);
+ });
+}
+
+/**
+ * Test IMapper::set(ChromaSiting)
+ */
+TEST_F(GraphicsMapperHidlTest, SetChromaSiting) {
+ ExtendableType chromaSiting = gralloc4::ChromaSiting_SitedInterstitial;
+ hidl_vec<uint8_t> vec;
+ ASSERT_EQ(NO_ERROR, gralloc4::encodeChromaSiting(chromaSiting, &vec));
+
+ testSet(mDummyDescriptorInfo, gralloc4::MetadataType_ChromaSiting, vec,
+ [&](const IMapper::BufferDescriptorInfo& /*info*/, const hidl_vec<uint8_t>& vec) {
+ ExtendableType realChromaSiting = gralloc4::ChromaSiting_None;
+ ASSERT_EQ(NO_ERROR, gralloc4::decodeChromaSiting(vec, &realChromaSiting));
+
+ EXPECT_EQ(chromaSiting.name, realChromaSiting.name);
+ EXPECT_EQ(chromaSiting.value, realChromaSiting.value);
+ });
+}
+
+/**
+ * Test IMapper::set(PlaneLayouts)
+ */
+TEST_F(GraphicsMapperHidlTest, SetPlaneLayouts) {
+ const native_handle_t* bufferHandle = nullptr;
+ auto info = mDummyDescriptorInfo;
+ ASSERT_NO_FATAL_FAILURE(bufferHandle = mGralloc->allocate(info, true));
+
+ std::vector<PlaneLayout> planeLayouts;
+ PlaneLayout planeLayoutA;
+ PlaneLayout planeLayoutRGB;
+ PlaneLayoutComponent component;
+
+ planeLayoutA.offsetInBytes = 0;
+ planeLayoutA.sampleIncrementInBits = 8;
+ planeLayoutA.strideInBytes = info.width + 20;
+ planeLayoutA.widthInSamples = info.width;
+ planeLayoutA.heightInSamples = info.height;
+ planeLayoutA.totalSizeInBytes = planeLayoutA.strideInBytes * info.height;
+ planeLayoutA.horizontalSubsampling = 1;
+ planeLayoutA.verticalSubsampling = 1;
+ planeLayoutA.crop.left = 0;
+ planeLayoutA.crop.top = 0;
+ planeLayoutA.crop.right = info.width;
+ planeLayoutA.crop.bottom = info.height;
+
+ component.type = gralloc4::PlaneLayoutComponentType_A;
+ component.offsetInBits = 0;
+ component.sizeInBits = 8;
+ planeLayoutA.components.push_back(component);
+
+ planeLayouts.push_back(planeLayoutA);
+
+ planeLayoutRGB.offsetInBytes = 0;
+ planeLayoutRGB.sampleIncrementInBits = 32;
+ planeLayoutRGB.strideInBytes = info.width + 20;
+ planeLayoutRGB.widthInSamples = info.width;
+ planeLayoutRGB.heightInSamples = info.height;
+ planeLayoutRGB.totalSizeInBytes = planeLayoutRGB.strideInBytes * info.height;
+ planeLayoutRGB.horizontalSubsampling = 1;
+ planeLayoutRGB.verticalSubsampling = 1;
+ planeLayoutRGB.crop.left = 0;
+ planeLayoutRGB.crop.top = 0;
+ planeLayoutRGB.crop.right = info.width;
+ planeLayoutRGB.crop.bottom = info.height;
+
+ component.type = gralloc4::PlaneLayoutComponentType_R;
+ planeLayoutRGB.components.push_back(component);
+ component.type = gralloc4::PlaneLayoutComponentType_G;
+ planeLayoutRGB.components.push_back(component);
+ component.type = gralloc4::PlaneLayoutComponentType_B;
+ planeLayoutRGB.components.push_back(component);
+
+ planeLayouts.push_back(planeLayoutRGB);
+
+ hidl_vec<uint8_t> vec;
+ ASSERT_EQ(NO_ERROR, gralloc4::encodePlaneLayouts(planeLayouts, &vec));
+
+ Error err = mGralloc->set(bufferHandle, gralloc4::MetadataType_PlaneLayouts, vec);
+ if (err == Error::UNSUPPORTED) {
+ GTEST_SUCCEED() << "setting this metadata is unsupported";
+ }
+ ASSERT_EQ(err, Error::NONE);
+
+ std::vector<PlaneLayout> realPlaneLayouts;
+ ASSERT_EQ(NO_ERROR, gralloc4::decodePlaneLayouts(vec, &realPlaneLayouts));
+
+ ASSERT_EQ(planeLayouts.size(), realPlaneLayouts.size());
+
+ for (int i = 0; i < realPlaneLayouts.size(); i++) {
+ const auto& planeLayout = planeLayouts[i];
+ const auto& realPlaneLayout = realPlaneLayouts[i];
+
+ EXPECT_EQ(planeLayout.offsetInBytes, realPlaneLayout.offsetInBytes);
+ EXPECT_EQ(planeLayout.sampleIncrementInBits, realPlaneLayout.sampleIncrementInBits);
+ EXPECT_EQ(planeLayout.strideInBytes, realPlaneLayout.strideInBytes);
+ EXPECT_EQ(planeLayout.widthInSamples, realPlaneLayout.widthInSamples);
+ EXPECT_EQ(planeLayout.heightInSamples, realPlaneLayout.heightInSamples);
+ EXPECT_LE(planeLayout.totalSizeInBytes, realPlaneLayout.totalSizeInBytes);
+ EXPECT_EQ(planeLayout.horizontalSubsampling, realPlaneLayout.horizontalSubsampling);
+ EXPECT_EQ(planeLayout.verticalSubsampling, realPlaneLayout.verticalSubsampling);
+
+ EXPECT_EQ(planeLayout.crop.left, realPlaneLayout.crop.left);
+ EXPECT_EQ(planeLayout.crop.top, realPlaneLayout.crop.top);
+ EXPECT_EQ(planeLayout.crop.right, realPlaneLayout.crop.right);
+ EXPECT_EQ(planeLayout.crop.bottom, realPlaneLayout.crop.bottom);
+
+ ASSERT_EQ(planeLayout.components.size(), realPlaneLayout.components.size());
+
+ for (int j = 0; j < realPlaneLayout.components.size(); j++) {
+ const auto& component = planeLayout.components[j];
+ const auto& realComponent = realPlaneLayout.components[j];
+
+ EXPECT_EQ(component.type.name, realComponent.type.name);
+ EXPECT_EQ(component.type.value, realComponent.type.value);
+ EXPECT_EQ(component.sizeInBits, realComponent.sizeInBits);
+ EXPECT_EQ(component.offsetInBits, realComponent.offsetInBits);
+ }
+ }
+}
+
+/**
+ * Test IMapper::set(Dataspace)
+ */
+TEST_F(GraphicsMapperHidlTest, SetDataspace) {
+ Dataspace dataspace = Dataspace::V0_SRGB_LINEAR;
+ hidl_vec<uint8_t> vec;
+ ASSERT_EQ(NO_ERROR, gralloc4::encodeDataspace(dataspace, &vec));
+
+ testSet(mDummyDescriptorInfo, gralloc4::MetadataType_Dataspace, vec,
+ [&](const IMapper::BufferDescriptorInfo& /*info*/, const hidl_vec<uint8_t>& vec) {
+ Dataspace realDataspace = Dataspace::UNKNOWN;
+ ASSERT_EQ(NO_ERROR, gralloc4::decodeDataspace(vec, &realDataspace));
+ EXPECT_EQ(dataspace, realDataspace);
+ });
+}
+
+/**
+ * Test IMapper::set(BlendMode)
+ */
+TEST_F(GraphicsMapperHidlTest, SetBlendMode) {
+ BlendMode blendMode = BlendMode::PREMULTIPLIED;
+ hidl_vec<uint8_t> vec;
+ ASSERT_EQ(NO_ERROR, gralloc4::encodeBlendMode(blendMode, &vec));
+
+ testSet(mDummyDescriptorInfo, gralloc4::MetadataType_BlendMode, vec,
+ [&](const IMapper::BufferDescriptorInfo& /*info*/, const hidl_vec<uint8_t>& vec) {
+ BlendMode realBlendMode = BlendMode::INVALID;
+ ASSERT_EQ(NO_ERROR, gralloc4::decodeBlendMode(vec, &realBlendMode));
+ EXPECT_EQ(blendMode, realBlendMode);
+ });
+}
+
+/**
+ * Test IMapper::set(metadata) with a bad buffer
+ */
+TEST_F(GraphicsMapperHidlTest, SetMetadataNullBuffer) {
+ const native_handle_t* bufferHandle = nullptr;
+ hidl_vec<uint8_t> vec;
+ ASSERT_EQ(Error::BAD_BUFFER, mGralloc->set(bufferHandle, gralloc4::MetadataType_BufferId, vec));
+ ASSERT_EQ(Error::BAD_BUFFER, mGralloc->set(bufferHandle, gralloc4::MetadataType_Name, vec));
+ ASSERT_EQ(Error::BAD_BUFFER, mGralloc->set(bufferHandle, gralloc4::MetadataType_Width, vec));
+ ASSERT_EQ(Error::BAD_BUFFER, mGralloc->set(bufferHandle, gralloc4::MetadataType_Height, vec));
+ ASSERT_EQ(Error::BAD_BUFFER,
+ mGralloc->set(bufferHandle, gralloc4::MetadataType_LayerCount, vec));
+ ASSERT_EQ(Error::BAD_BUFFER,
+ mGralloc->set(bufferHandle, gralloc4::MetadataType_PixelFormatRequested, vec));
+ ASSERT_EQ(Error::BAD_BUFFER,
+ mGralloc->set(bufferHandle, gralloc4::MetadataType_PixelFormatFourCC, vec));
+ ASSERT_EQ(Error::BAD_BUFFER,
+ mGralloc->set(bufferHandle, gralloc4::MetadataType_PixelFormatModifier, vec));
+ ASSERT_EQ(Error::BAD_BUFFER, mGralloc->set(bufferHandle, gralloc4::MetadataType_Usage, vec));
+ ASSERT_EQ(Error::BAD_BUFFER,
+ mGralloc->set(bufferHandle, gralloc4::MetadataType_AllocationSize, vec));
+ ASSERT_EQ(Error::BAD_BUFFER,
+ mGralloc->set(bufferHandle, gralloc4::MetadataType_ProtectedContent, vec));
+ ASSERT_EQ(Error::BAD_BUFFER,
+ mGralloc->set(bufferHandle, gralloc4::MetadataType_Compression, vec));
+ ASSERT_EQ(Error::BAD_BUFFER,
+ mGralloc->set(bufferHandle, gralloc4::MetadataType_Interlaced, vec));
+ ASSERT_EQ(Error::BAD_BUFFER,
+ mGralloc->set(bufferHandle, gralloc4::MetadataType_ChromaSiting, vec));
+ ASSERT_EQ(Error::BAD_BUFFER,
+ mGralloc->set(bufferHandle, gralloc4::MetadataType_PlaneLayouts, vec));
+ ASSERT_EQ(Error::BAD_BUFFER,
+ mGralloc->set(bufferHandle, gralloc4::MetadataType_Dataspace, vec));
+ ASSERT_EQ(Error::BAD_BUFFER,
+ mGralloc->set(bufferHandle, gralloc4::MetadataType_BlendMode, vec));
+}
+
+/**
+ * Test IMapper::set(metadata) for constant metadata
+ */
+TEST_F(GraphicsMapperHidlTest, SetConstantMetadata) {
+ const native_handle_t* bufferHandle = nullptr;
+ ASSERT_NO_FATAL_FAILURE(bufferHandle = mGralloc->allocate(mDummyDescriptorInfo, true));
+
+ hidl_vec<uint8_t> vec;
+ ASSERT_EQ(Error::BAD_VALUE, mGralloc->set(bufferHandle, gralloc4::MetadataType_BufferId, vec));
+ ASSERT_EQ(Error::BAD_VALUE, mGralloc->set(bufferHandle, gralloc4::MetadataType_Name, vec));
+ ASSERT_EQ(Error::BAD_VALUE, mGralloc->set(bufferHandle, gralloc4::MetadataType_Width, vec));
+ ASSERT_EQ(Error::BAD_VALUE, mGralloc->set(bufferHandle, gralloc4::MetadataType_Height, vec));
+ ASSERT_EQ(Error::BAD_VALUE,
+ mGralloc->set(bufferHandle, gralloc4::MetadataType_LayerCount, vec));
+ ASSERT_EQ(Error::BAD_VALUE,
+ mGralloc->set(bufferHandle, gralloc4::MetadataType_PixelFormatRequested, vec));
+ ASSERT_EQ(Error::BAD_VALUE, mGralloc->set(bufferHandle, gralloc4::MetadataType_Usage, vec));
+}
+
+/**
+ * Test IMapper::set(metadata) for bad metadata
+ */
+TEST_F(GraphicsMapperHidlTest, SetBadMetadata) {
+ const native_handle_t* bufferHandle = nullptr;
+ ASSERT_NO_FATAL_FAILURE(bufferHandle = mGralloc->allocate(mDummyDescriptorInfo, true));
+
+ hidl_vec<uint8_t> vec;
+ ASSERT_EQ(Error::UNSUPPORTED,
+ mGralloc->set(bufferHandle, gralloc4::MetadataType_BufferId, vec));
+ ASSERT_EQ(Error::UNSUPPORTED, mGralloc->set(bufferHandle, gralloc4::MetadataType_Name, vec));
+ ASSERT_EQ(Error::UNSUPPORTED, mGralloc->set(bufferHandle, gralloc4::MetadataType_Width, vec));
+ ASSERT_EQ(Error::UNSUPPORTED, mGralloc->set(bufferHandle, gralloc4::MetadataType_Height, vec));
+ ASSERT_EQ(Error::UNSUPPORTED,
+ mGralloc->set(bufferHandle, gralloc4::MetadataType_LayerCount, vec));
+ ASSERT_EQ(Error::UNSUPPORTED,
+ mGralloc->set(bufferHandle, gralloc4::MetadataType_PixelFormatRequested, vec));
+ ASSERT_EQ(Error::UNSUPPORTED,
+ mGralloc->set(bufferHandle, gralloc4::MetadataType_PixelFormatFourCC, vec));
+ ASSERT_EQ(Error::UNSUPPORTED,
+ mGralloc->set(bufferHandle, gralloc4::MetadataType_PixelFormatModifier, vec));
+ ASSERT_EQ(Error::UNSUPPORTED, mGralloc->set(bufferHandle, gralloc4::MetadataType_Usage, vec));
+ ASSERT_EQ(Error::UNSUPPORTED,
+ mGralloc->set(bufferHandle, gralloc4::MetadataType_AllocationSize, vec));
+ ASSERT_EQ(Error::UNSUPPORTED,
+ mGralloc->set(bufferHandle, gralloc4::MetadataType_ProtectedContent, vec));
+ ASSERT_EQ(Error::UNSUPPORTED,
+ mGralloc->set(bufferHandle, gralloc4::MetadataType_Compression, vec));
+ ASSERT_EQ(Error::UNSUPPORTED,
+ mGralloc->set(bufferHandle, gralloc4::MetadataType_Interlaced, vec));
+ ASSERT_EQ(Error::UNSUPPORTED,
+ mGralloc->set(bufferHandle, gralloc4::MetadataType_ChromaSiting, vec));
+ ASSERT_EQ(Error::UNSUPPORTED,
+ mGralloc->set(bufferHandle, gralloc4::MetadataType_PlaneLayouts, vec));
+ ASSERT_EQ(Error::UNSUPPORTED,
+ mGralloc->set(bufferHandle, gralloc4::MetadataType_Dataspace, vec));
+ ASSERT_EQ(Error::UNSUPPORTED,
+ mGralloc->set(bufferHandle, gralloc4::MetadataType_BlendMode, vec));
+}
+
+/**
+ * Test IMapper::getFromBufferDescriptorInfo(BufferId)
+ */
+TEST_F(GraphicsMapperHidlTest, GetFromBufferDescriptorInfoBufferId) {
+ hidl_vec<uint8_t> vec;
+ ASSERT_EQ(Error::UNSUPPORTED,
+ mGralloc->getFromBufferDescriptorInfo(mDummyDescriptorInfo,
+ gralloc4::MetadataType_BufferId, &vec));
+}
+
+/**
+ * Test IMapper::getFromBufferDescriptorInfo(Name)
+ */
+TEST_F(GraphicsMapperHidlTest, GetFromBufferDescriptorInfoName) {
+ hidl_vec<uint8_t> vec;
+ ASSERT_EQ(Error::NONE, mGralloc->getFromBufferDescriptorInfo(
+ mDummyDescriptorInfo, gralloc4::MetadataType_Name, &vec));
+
+ std::string name;
+ ASSERT_EQ(NO_ERROR, gralloc4::decodeName(vec, &name));
+ EXPECT_EQ(mDummyDescriptorInfo.name, name);
+}
+
+/**
+ * Test IMapper::getFromBufferDescriptorInfo(Width)
+ */
+TEST_F(GraphicsMapperHidlTest, GetFromBufferDescriptorInfoWidth) {
+ hidl_vec<uint8_t> vec;
+ ASSERT_EQ(Error::NONE, mGralloc->getFromBufferDescriptorInfo(
+ mDummyDescriptorInfo, gralloc4::MetadataType_Width, &vec));
+
+ uint64_t width = 0;
+ ASSERT_EQ(NO_ERROR, gralloc4::decodeWidth(vec, &width));
+ EXPECT_EQ(mDummyDescriptorInfo.width, width);
+}
+
+/**
+ * Test IMapper::getFromBufferDescriptorInfo(Height)
+ */
+TEST_F(GraphicsMapperHidlTest, GetFromBufferDescriptorInfoHeight) {
+ hidl_vec<uint8_t> vec;
+ ASSERT_EQ(Error::NONE, mGralloc->getFromBufferDescriptorInfo(
+ mDummyDescriptorInfo, gralloc4::MetadataType_Height, &vec));
+
+ uint64_t height = 0;
+ ASSERT_EQ(NO_ERROR, gralloc4::decodeHeight(vec, &height));
+ EXPECT_EQ(mDummyDescriptorInfo.height, height);
+}
+
+/**
+ * Test IMapper::getFromBufferDescriptorInfo(PixelFormatRequested)
+ */
+TEST_F(GraphicsMapperHidlTest, GetFromBufferDescriptorInfoPixelFormatRequested) {
+ hidl_vec<uint8_t> vec;
+ ASSERT_EQ(Error::NONE,
+ mGralloc->getFromBufferDescriptorInfo(
+ mDummyDescriptorInfo, gralloc4::MetadataType_PixelFormatRequested, &vec));
+
+ PixelFormat pixelFormatRequested = PixelFormat::BLOB;
+ ASSERT_EQ(NO_ERROR, gralloc4::decodePixelFormatRequested(vec, &pixelFormatRequested));
+ EXPECT_EQ(mDummyDescriptorInfo.format, pixelFormatRequested);
+}
+
+/**
+ * Test IMapper::getFromBufferDescriptorInfo(PixelFormatFourCC)
+ */
+TEST_F(GraphicsMapperHidlTest, GetFromBufferDescriptorInfoPixelFormatFourCC) {
+ hidl_vec<uint8_t> vec;
+ Error err = mGralloc->getFromBufferDescriptorInfo(
+ mDummyDescriptorInfo, gralloc4::MetadataType_PixelFormatFourCC, &vec);
+ if (err == Error::UNSUPPORTED) {
+ GTEST_SUCCEED() << "setting this metadata is unsupported";
+ }
+ ASSERT_EQ(err, Error::NONE);
+
+ uint32_t pixelFormatFourCC = 0;
+ ASSERT_EQ(NO_ERROR, gralloc4::decodePixelFormatFourCC(vec, &pixelFormatFourCC));
+}
+
+/**
+ * Test IMapper::getFromBufferDescriptorInfo(PixelFormatModifier)
+ */
+TEST_F(GraphicsMapperHidlTest, GetFromBufferDescriptorInfoPixelFormatModifier) {
+ hidl_vec<uint8_t> vec;
+ Error err = mGralloc->getFromBufferDescriptorInfo(
+ mDummyDescriptorInfo, gralloc4::MetadataType_PixelFormatModifier, &vec);
+ if (err == Error::UNSUPPORTED) {
+ GTEST_SUCCEED() << "setting this metadata is unsupported";
+ }
+ ASSERT_EQ(err, Error::NONE);
+
+ uint64_t pixelFormatModifier = 0;
+ ASSERT_EQ(NO_ERROR, gralloc4::decodePixelFormatModifier(vec, &pixelFormatModifier));
+}
+
+/**
+ * Test IMapper::getFromBufferDescriptorInfo(Usage)
+ */
+TEST_F(GraphicsMapperHidlTest, GetFromBufferDescriptorInfoUsage) {
+ hidl_vec<uint8_t> vec;
+ ASSERT_EQ(Error::NONE, mGralloc->getFromBufferDescriptorInfo(
+ mDummyDescriptorInfo, gralloc4::MetadataType_Usage, &vec));
+
+ uint64_t usage = 0;
+ ASSERT_EQ(NO_ERROR, gralloc4::decodeUsage(vec, &usage));
+ EXPECT_EQ(mDummyDescriptorInfo.usage, usage);
+}
+
+/**
+ * Test IMapper::getFromBufferDescriptorInfo(AllocationSize)
+ */
+TEST_F(GraphicsMapperHidlTest, GetFromBufferDescriptorInfoAllocationSize) {
+ hidl_vec<uint8_t> vec;
+ Error err = mGralloc->getFromBufferDescriptorInfo(mDummyDescriptorInfo,
+ gralloc4::MetadataType_AllocationSize, &vec);
+ if (err == Error::UNSUPPORTED) {
+ GTEST_SUCCEED() << "setting this metadata is unsupported";
+ }
+ ASSERT_EQ(err, Error::NONE);
+
+ uint64_t allocationSize = 0;
+ ASSERT_EQ(NO_ERROR, gralloc4::decodeAllocationSize(vec, &allocationSize));
+}
+
+/**
+ * Test IMapper::getFromBufferDescriptorInfo(ProtectedContent)
+ */
+TEST_F(GraphicsMapperHidlTest, GetFromBufferDescriptorInfoProtectedContent) {
+ auto info = mDummyDescriptorInfo;
+ info.usage = BufferUsage::PROTECTED | BufferUsage::COMPOSER_OVERLAY;
+
+ hidl_vec<uint8_t> vec;
+ ASSERT_EQ(Error::NONE, mGralloc->getFromBufferDescriptorInfo(
+ info, gralloc4::MetadataType_ProtectedContent, &vec));
+
+ uint64_t protectedContent = 0;
+ ASSERT_EQ(NO_ERROR, gralloc4::decodeProtectedContent(vec, &protectedContent));
+ EXPECT_EQ(1, protectedContent);
+}
+
+/**
+ * Test IMapper::getFromBufferDescriptorInfo(Compression)
+ */
+TEST_F(GraphicsMapperHidlTest, GetFromBufferDescriptorInfoCompression) {
+ auto info = mDummyDescriptorInfo;
+ info.usage = static_cast<uint64_t>(BufferUsage::CPU_WRITE_OFTEN | BufferUsage::CPU_READ_OFTEN);
+
+ hidl_vec<uint8_t> vec;
+ ASSERT_EQ(Error::NONE, mGralloc->getFromBufferDescriptorInfo(
+ info, gralloc4::MetadataType_Compression, &vec));
+
+ ExtendableType compression = gralloc4::Compression_DisplayStreamCompression;
+ ASSERT_EQ(NO_ERROR, gralloc4::decodeCompression(vec, &compression));
+
+ EXPECT_EQ(gralloc4::Compression_None.name, compression.name);
+ EXPECT_EQ(gralloc4::Compression_None.value, compression.value);
+}
+
+/**
+ * Test IMapper::getFromBufferDescriptorInfo(Interlaced)
+ */
+TEST_F(GraphicsMapperHidlTest, GetFromBufferDescriptorInfoInterlaced) {
+ hidl_vec<uint8_t> vec;
+ ASSERT_EQ(Error::NONE, mGralloc->getFromBufferDescriptorInfo(
+ mDummyDescriptorInfo, gralloc4::MetadataType_Interlaced, &vec));
+
+ ExtendableType interlaced = gralloc4::Interlaced_TopBottom;
+ ASSERT_EQ(NO_ERROR, gralloc4::decodeInterlaced(vec, &interlaced));
+
+ EXPECT_EQ(gralloc4::Interlaced_None.name, interlaced.name);
+ EXPECT_EQ(gralloc4::Interlaced_None.value, interlaced.value);
+}
+
+/**
+ * Test IMapper::getFromBufferDescriptorInfo(ChromaSiting)
+ */
+TEST_F(GraphicsMapperHidlTest, GetFromBufferDescriptorInfoChromaSiting) {
+ hidl_vec<uint8_t> vec;
+ ASSERT_EQ(Error::NONE,
+ mGralloc->getFromBufferDescriptorInfo(mDummyDescriptorInfo,
+ gralloc4::MetadataType_ChromaSiting, &vec));
+
+ ExtendableType chromaSiting = gralloc4::ChromaSiting_CositedHorizontal;
+ ASSERT_EQ(NO_ERROR, gralloc4::decodeChromaSiting(vec, &chromaSiting));
+
+ EXPECT_EQ(gralloc4::ChromaSiting_None.name, chromaSiting.name);
+ EXPECT_EQ(gralloc4::ChromaSiting_None.value, chromaSiting.value);
+}
+
+/**
+ * Test IMapper::getFromBufferDescriptorInfo(PlaneLayouts)
+ */
+TEST_F(GraphicsMapperHidlTest, GetFromBufferDescriptorInfoPlaneLayouts) {
+ hidl_vec<uint8_t> vec;
+ ASSERT_EQ(Error::NONE,
+ mGralloc->getFromBufferDescriptorInfo(mDummyDescriptorInfo,
+ gralloc4::MetadataType_PlaneLayouts, &vec));
+
+ std::vector<PlaneLayout> planeLayouts;
+ ASSERT_EQ(NO_ERROR, gralloc4::decodePlaneLayouts(vec, &planeLayouts));
+ ASSERT_NO_FATAL_FAILURE(verifyDummyDescriptorInfoPlaneLayouts(planeLayouts));
+}
+
+/**
+ * Test IMapper::getFromBufferDescriptorInfo(Dataspace)
+ */
+TEST_F(GraphicsMapperHidlTest, GetFromBufferDescriptorInfoDataspace) {
+ hidl_vec<uint8_t> vec;
+ ASSERT_EQ(Error::NONE, mGralloc->getFromBufferDescriptorInfo(
+ mDummyDescriptorInfo, gralloc4::MetadataType_Dataspace, &vec));
+
+ Dataspace dataspace = Dataspace::DISPLAY_P3;
+ ASSERT_EQ(NO_ERROR, gralloc4::decodeDataspace(vec, &dataspace));
+ EXPECT_EQ(Dataspace::UNKNOWN, dataspace);
+}
+
+/**
+ * Test IMapper::getFromBufferDescriptorInfo(BlendMode)
+ */
+TEST_F(GraphicsMapperHidlTest, GetFromBufferDescriptorInfoBlendMode) {
+ hidl_vec<uint8_t> vec;
+ ASSERT_EQ(Error::NONE, mGralloc->getFromBufferDescriptorInfo(
+ mDummyDescriptorInfo, gralloc4::MetadataType_BlendMode, &vec));
+
+ BlendMode blendMode = BlendMode::COVERAGE;
+ ASSERT_EQ(NO_ERROR, gralloc4::decodeBlendMode(vec, &blendMode));
+ EXPECT_EQ(BlendMode::INVALID, blendMode);
+}
+
+/**
+ * Test IMapper::getFromBufferDescriptorInfo(metadata) for unsupported metadata
+ */
+TEST_F(GraphicsMapperHidlTest, GetFromBufferDescriptorInfoUnsupportedMetadata) {
+ MetadataType metadataTypeFake = {"FAKE", 1};
+
+ hidl_vec<uint8_t> vec;
+ ASSERT_EQ(Error::UNSUPPORTED,
+ mGralloc->getFromBufferDescriptorInfo(mDummyDescriptorInfo, metadataTypeFake, &vec));
+ ASSERT_EQ(0, vec.size());
+}
+
+/**
+ * Test IMapper::getFromBufferDescriptorInfo(metadata) for unsupported standard metadata
+ */
+TEST_F(GraphicsMapperHidlTest, GetFromBufferDescriptorInfoUnsupportedStandardMetadata) {
+ MetadataType metadataTypeFake = {GRALLOC4_STANDARD_METADATA_TYPE, 9999};
+
+ hidl_vec<uint8_t> vec;
+ ASSERT_EQ(Error::UNSUPPORTED,
+ mGralloc->getFromBufferDescriptorInfo(mDummyDescriptorInfo, metadataTypeFake, &vec));
+ ASSERT_EQ(0, vec.size());
+}
+
+/**
+ * Test IMapper::listSupportedMetadataTypes()
+ */
+TEST_F(GraphicsMapperHidlTest, ListSupportedMetadataTypes) {
+ hidl_vec<IMapper::MetadataTypeDescription> descriptions;
+ mGralloc->getMapper()->listSupportedMetadataTypes(
+ [&](const auto& tmpError, const auto& tmpDescriptions) {
+ ASSERT_EQ(Error::NONE, tmpError);
+ descriptions = tmpDescriptions;
+ });
+
+ std::set<StandardMetadataType> foundMetadataTypes;
+
+ std::set<StandardMetadataType> notSettableMetadataTypes{
+ StandardMetadataType::BUFFER_ID, StandardMetadataType::NAME,
+ StandardMetadataType::WIDTH, StandardMetadataType::HEIGHT,
+ StandardMetadataType::LAYER_COUNT, StandardMetadataType::PIXEL_FORMAT_REQUESTED,
+ StandardMetadataType::USAGE};
+
+ ASSERT_LE(sRequiredMetadataTypes.size(), descriptions.size());
+
+ for (const auto& description : descriptions) {
+ const auto& metadataType = description.metadataType;
+
+ if (!gralloc4::isStandardMetadataType(metadataType)) {
+ EXPECT_GT(0, description.description.size());
+ continue;
+ }
+
+ StandardMetadataType type = gralloc4::getStandardMetadataTypeValue(metadataType);
+
+ if (sRequiredMetadataTypes.find(type) == sRequiredMetadataTypes.end()) {
+ continue;
+ }
+
+ ASSERT_EQ(foundMetadataTypes.find(type), foundMetadataTypes.end());
+ foundMetadataTypes.insert(type);
+
+ ASSERT_TRUE(description.isGettable);
+
+ if (notSettableMetadataTypes.find(type) != notSettableMetadataTypes.end()) {
+ ASSERT_FALSE(description.isSettable);
+ }
+ }
+
+ ASSERT_EQ(sRequiredMetadataTypes, foundMetadataTypes);
+}
+
} // namespace
} // namespace vts
} // namespace V4_0
diff --git a/keymaster/4.1/Android.bp b/keymaster/4.1/Android.bp
new file mode 100644
index 0000000..eaa7e41
--- /dev/null
+++ b/keymaster/4.1/Android.bp
@@ -0,0 +1,19 @@
+// This file is autogenerated by hidl-gen -Landroidbp.
+
+hidl_interface {
+ name: "android.hardware.keymaster@4.1",
+ root: "android.hardware",
+ vndk: {
+ enabled: true,
+ },
+ srcs: [
+ "types.hal",
+ "IKeymasterDevice.hal",
+ "IOperation.hal",
+ ],
+ interfaces: [
+ "android.hardware.keymaster@4.0",
+ "android.hidl.base@1.0",
+ ],
+ gen_java: false,
+}
diff --git a/keymaster/4.1/IKeymasterDevice.hal b/keymaster/4.1/IKeymasterDevice.hal
new file mode 100644
index 0000000..64d2c9f
--- /dev/null
+++ b/keymaster/4.1/IKeymasterDevice.hal
@@ -0,0 +1,70 @@
+/*
+ * 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.
+ */
+
+package android.hardware.keymaster@4.1;
+
+import @4.0::ErrorCode;
+import @4.0::HardwareAuthToken;
+import @4.0::IKeymasterDevice;
+import @4.0::KeyParameter;
+import @4.0::KeyPurpose;
+import @4.0::OperationHandle;
+import IOperation;
+
+/**
+ * @4.1::IKeymasterDevice is a minor extension to @4.0::IKeymasterDevice. It adds support for
+ *
+ * - Partial hardware enforcment of UNLOCKED_DEVICE_REQUIRED keys;
+ * - Device-unique attestaion;
+ * - Early boot only keys;
+ * - Better cleanup of operations when clients die without completing or aborting them.
+ */
+interface IKeymasterDevice extends @4.0::IKeymasterDevice {
+ /**
+ * Called by client to notify the IKeymasterDevice that the device is now locked, and keys with
+ * the UNLOCKED_DEVICE_REQUIRED tag should no longer be usable. When this function is called,
+ * the IKeymasterDevice should note the current timestamp, and attempts to use
+ * UNLOCKED_DEVICE_REQUIRED keys must be rejected with Error::DEVICE_LOCKED until an
+ * authentication token with a later timestamp is presented. If the `passwordOnly' argument is
+ * set to true the sufficiently-recent authentication token must indicate that the user
+ * authenticated with a password, not a biometric.
+ *
+ * @param passwordOnly specifies whether the device must be unlocked with a password, rather
+ * than a biometric, before UNLOCKED_DEVICE_REQUIRED keys can be used.
+ */
+ deviceLocked(bool passwordOnly) generates (ErrorCode error);
+
+ /**
+ * Called by client to notify the IKeymasterDevice that the device has left the early boot
+ * state, and that keys with the EARLY_BOOT_ONLY tag may no longer be used. All attempts to use
+ * an EARLY_BOOT_ONLY key after this method is called must fail with Error::INVALID_KEY_BLOB.
+ */
+ earlyBootEnded() generates (ErrorCode error);
+
+ /**
+ * Begins a cryptographic operation. beginOp() is a variation on begin(). beginOp() has
+ * identical functionality to begin, but instead of an OperationHandle it returns an IOperation
+ * object. An IKeymasterDevice HAL service must call linkToDeath() on the Operation before
+ * returning it, and the provided hidl_death_recipient, if called, must abort() the operation.
+ * This is to ensure that in the event a client crashes while an operation is in progress, the
+ * operation slot is freed and available for use by other clients.
+ *
+ * @4.1::IKeymasterDevices must implement both beginOp() and begin().
+ */
+ beginOp(KeyPurpose purpose, vec<uint8_t> keyBlob, vec<KeyParameter> inParams,
+ HardwareAuthToken authToken)
+ generates (ErrorCode error, vec<KeyParameter> outParam, IOperation operation);
+};
diff --git a/keymaster/4.1/IOperation.hal b/keymaster/4.1/IOperation.hal
new file mode 100644
index 0000000..7103e9e
--- /dev/null
+++ b/keymaster/4.1/IOperation.hal
@@ -0,0 +1,31 @@
+/*
+ * 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.
+ */
+
+package android.hardware.keymaster@4.1;
+
+import @4.0::ErrorCode;
+import @4.0::OperationHandle;
+
+/**
+ * IOperation represents an in-progress IKeymasterDevice operation. It is returned by
+ * IKeymasterDevice.beginOp().
+ */
+interface IOperation {
+ /**
+ * Returns the operation handle to be used as an authentication challenge.
+ */
+ getOperationChallenge() generates (ErrorCode error, OperationHandle operation);
+};
diff --git a/keymaster/4.1/default/Android.bp b/keymaster/4.1/default/Android.bp
new file mode 100644
index 0000000..b06878b
--- /dev/null
+++ b/keymaster/4.1/default/Android.bp
@@ -0,0 +1,38 @@
+//
+// 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.
+//
+
+cc_binary {
+ name: "android.hardware.keymaster@4.1-service",
+ defaults: ["hidl_defaults"],
+ relative_install_path: "hw",
+ vendor: true,
+ init_rc: ["android.hardware.keymaster@4.1-service.rc"],
+ srcs: ["service.cpp"],
+
+ shared_libs: [
+ "android.hardware.keymaster@4.0",
+ "android.hardware.keymaster@4.1",
+ "libbase",
+ "libcutils",
+ "libhardware",
+ "libhidlbase",
+ "libkeymaster4",
+ "libkeymaster41",
+ "liblog",
+ "libutils",
+ ],
+
+}
diff --git a/keymaster/4.1/default/OWNERS b/keymaster/4.1/default/OWNERS
new file mode 100644
index 0000000..335660d
--- /dev/null
+++ b/keymaster/4.1/default/OWNERS
@@ -0,0 +1,2 @@
+jdanis@google.com
+swillden@google.com
diff --git a/keymaster/4.1/default/android.hardware.keymaster@4.1-service.rc b/keymaster/4.1/default/android.hardware.keymaster@4.1-service.rc
new file mode 100644
index 0000000..740b3c2
--- /dev/null
+++ b/keymaster/4.1/default/android.hardware.keymaster@4.1-service.rc
@@ -0,0 +1,6 @@
+service vendor.keymaster-4-1 /vendor/bin/hw/android.hardware.keymaster@4.1-service
+ interface android.hardware.keymaster@4.0::IKeymasterDevice default
+ interface android.hardware.keymaster@4.1::IKeymasterDevice default
+ class early_hal
+ user system
+ group system drmrpc
diff --git a/keymaster/4.1/default/service.cpp b/keymaster/4.1/default/service.cpp
new file mode 100644
index 0000000..d79a291
--- /dev/null
+++ b/keymaster/4.1/default/service.cpp
@@ -0,0 +1,35 @@
+/*
+** Copyright 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.
+*/
+
+#include <android-base/logging.h>
+#include <android/hardware/keymaster/4.1/IKeymasterDevice.h>
+#include <hidl/HidlTransportSupport.h>
+
+#include <AndroidKeymaster41Device.h>
+
+using android::hardware::keymaster::V4_0::SecurityLevel;
+
+int main() {
+ ::android::hardware::configureRpcThreadpool(1, true /* willJoinThreadpool */);
+ auto keymaster = ::keymaster::V4_1::CreateKeymasterDevice(SecurityLevel::SOFTWARE);
+ auto status = keymaster->registerAsService();
+ if (status != android::OK) {
+ LOG(FATAL) << "Could not register service for Keymaster 4.1 (" << status << ")";
+ }
+
+ android::hardware::joinRpcThreadpool();
+ return -1; // Should never get here.
+}
diff --git a/keymaster/4.1/support/Android.bp b/keymaster/4.1/support/Android.bp
new file mode 100644
index 0000000..34b6108
--- /dev/null
+++ b/keymaster/4.1/support/Android.bp
@@ -0,0 +1,32 @@
+//
+// 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.
+//
+
+cc_library {
+ name: "libkeymaster4_1support",
+ vendor_available: true,
+ cflags: [
+ "-Wall",
+ "-Wextra",
+ "-Werror",
+ ],
+ export_include_dirs: ["include"],
+ shared_libs: [
+ "android.hardware.keymaster@3.0",
+ "android.hardware.keymaster@4.0",
+ "android.hardware.keymaster@4.1",
+ "libkeymaster4support",
+ ]
+}
diff --git a/keymaster/4.1/support/include/keymasterV4_1/authorization_set.h b/keymaster/4.1/support/include/keymasterV4_1/authorization_set.h
new file mode 100644
index 0000000..afc0eaf
--- /dev/null
+++ b/keymaster/4.1/support/include/keymasterV4_1/authorization_set.h
@@ -0,0 +1,32 @@
+/*
+ * 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.
+ */
+
+#ifndef HARDWARE_INTERFACES_KEYMASTER_V4_1_SUPPORT_INCLUDE_AUTHORIZATION_SET_H_
+#define HARDWARE_INTERFACES_KEYMASTER_V4_1_SUPPORT_INCLUDE_AUTHORIZATION_SET_H_
+
+#include <keymasterV4_0/authorization_set.h>
+
+#include <keymasterV4_1/keymaster_tags.h>
+
+namespace android::hardware::keymaster::V4_1 {
+
+using V4_0::AuthorizationSet;
+using V4_0::AuthorizationSetBuilder;
+using V4_0::KeyParameter;
+
+} // namespace android::hardware::keymaster::V4_1
+
+#endif // HARDWARE_INTERFACES_KEYMASTER_V4_1_SUPPORT_INCLUDE_AUTHORIZATION_SET_H_
diff --git a/keymaster/4.1/support/include/keymasterV4_1/keymaster_tags.h b/keymaster/4.1/support/include/keymasterV4_1/keymaster_tags.h
new file mode 100644
index 0000000..6ffe8e1
--- /dev/null
+++ b/keymaster/4.1/support/include/keymasterV4_1/keymaster_tags.h
@@ -0,0 +1,96 @@
+/*
+ * 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.
+ */
+
+#ifndef HARDWARE_INTERFACES_KEYMASTER_V4_1_SUPPORT_INCLUDE_KEYMASTER_TAGS_H_
+#define HARDWARE_INTERFACES_KEYMASTER_V4_1_SUPPORT_INCLUDE_KEYMASTER_TAGS_H_
+
+#include <android/hardware/keymaster/4.1/types.h>
+
+#include <keymasterV4_0/keymaster_tags.h>
+
+namespace android::hardware::keymaster::V4_1 {
+
+using V4_0::BlockMode;
+using V4_0::Digest;
+using V4_0::EcCurve;
+using V4_0::ErrorCode;
+using V4_0::HardwareAuthToken;
+using V4_0::KeyParameter;
+using V4_0::PaddingMode;
+using V4_0::TagType;
+using V4_0::VerificationToken;
+
+using V4_0::TypedTag;
+
+using V4_0::TAG_ACTIVE_DATETIME;
+using V4_0::TAG_ALGORITHM;
+using V4_0::TAG_ALLOW_WHILE_ON_BODY;
+using V4_0::TAG_APPLICATION_DATA;
+using V4_0::TAG_APPLICATION_ID;
+using V4_0::TAG_ASSOCIATED_DATA;
+using V4_0::TAG_ATTESTATION_APPLICATION_ID;
+using V4_0::TAG_ATTESTATION_CHALLENGE;
+using V4_0::TAG_AUTH_TIMEOUT;
+using V4_0::TAG_BLOB_USAGE_REQUIREMENTS;
+using V4_0::TAG_BLOCK_MODE;
+using V4_0::TAG_BOOT_PATCHLEVEL;
+using V4_0::TAG_BOOTLOADER_ONLY;
+using V4_0::TAG_CALLER_NONCE;
+using V4_0::TAG_CONFIRMATION_TOKEN;
+using V4_0::TAG_CREATION_DATETIME;
+using V4_0::TAG_DIGEST;
+using V4_0::TAG_EC_CURVE;
+using V4_0::TAG_HARDWARE_TYPE;
+using V4_0::TAG_INCLUDE_UNIQUE_ID;
+using V4_0::TAG_INVALID;
+using V4_0::TAG_KEY_SIZE;
+using V4_0::TAG_MAC_LENGTH;
+using V4_0::TAG_MAX_USES_PER_BOOT;
+using V4_0::TAG_MIN_MAC_LENGTH;
+using V4_0::TAG_MIN_SECONDS_BETWEEN_OPS;
+using V4_0::TAG_NO_AUTH_REQUIRED;
+using V4_0::TAG_NONCE;
+using V4_0::TAG_ORIGIN;
+using V4_0::TAG_ORIGINATION_EXPIRE_DATETIME;
+using V4_0::TAG_OS_PATCHLEVEL;
+using V4_0::TAG_OS_VERSION;
+using V4_0::TAG_PADDING;
+using V4_0::TAG_PURPOSE;
+using V4_0::TAG_RESET_SINCE_ID_ROTATION;
+using V4_0::TAG_ROLLBACK_RESISTANCE;
+using V4_0::TAG_ROOT_OF_TRUST;
+using V4_0::TAG_RSA_PUBLIC_EXPONENT;
+using V4_0::TAG_TRUSTED_CONFIRMATION_REQUIRED;
+using V4_0::TAG_TRUSTED_USER_PRESENCE_REQUIRED;
+using V4_0::TAG_UNIQUE_ID;
+using V4_0::TAG_UNLOCKED_DEVICE_REQUIRED;
+using V4_0::TAG_USAGE_EXPIRE_DATETIME;
+using V4_0::TAG_USER_AUTH_TYPE;
+using V4_0::TAG_USER_ID;
+using V4_0::TAG_USER_SECURE_ID;
+using V4_0::TAG_VENDOR_PATCHLEVEL;
+
+#define DECLARE_KM_4_1_TYPED_TAG(name) \
+ typedef typename V4_0::Tag2TypedTag<(static_cast<V4_0::Tag>(V4_1::Tag::name))>::type \
+ TAG_##name##_t; \
+ static TAG_##name##_t TAG_##name;
+
+DECLARE_KM_4_1_TYPED_TAG(EARLY_BOOT_ONLY);
+DECLARE_KM_4_1_TYPED_TAG(DEVICE_UNIQUE_ATTESTATION);
+
+} // namespace android::hardware::keymaster::V4_1
+
+#endif // HARDWARE_INTERFACES_KEYMASTER_V4_1_SUPPORT_INCLUDE_KEYMASTER_TAGS_H_
diff --git a/keymaster/4.1/types.hal b/keymaster/4.1/types.hal
new file mode 100644
index 0000000..bdf1731
--- /dev/null
+++ b/keymaster/4.1/types.hal
@@ -0,0 +1,42 @@
+/*
+ * 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.
+ */
+
+package android.hardware.keymaster@4.1;
+
+import @4.0::ErrorCode;
+import @4.0::Tag;
+import @4.0::TagType;
+
+enum Tag : @4.0::Tag {
+ /**
+ * Keys tagged with EARLY_BOOT_ONLY may only be used, or created, during early boot, until
+ * IKeymasterDevice::earlyBootEnded() is called.
+ */
+ EARLY_BOOT_ONLY = TagType:BOOL | 305,
+ /**
+ * DEVICE_UNIQUE_ATTESTATION is an argument to IKeymasterDevice::attestKey(). It indicates that
+ * attestation using a device-unique key is requested, rather than a batch key. Only
+ * SecurityLevel::STRONGBOX IKeymasterDevices may support device-unique attestations.
+ * SecurityLevel::TRUSTED_ENVIRONMENT IKeymasterDevices must return ErrorCode::INVALID_ARGUMENT
+ * if they receive DEVICE_UNIQUE_ATTESTATION. SecurityLevel::STRONGBOX IKeymasterDevices need
+ * not support DEVICE_UNIQUE_ATTESTATION, and return ErrorCode::CANNOT_ATTEST_IDS if they do not
+ * support it.
+ *
+ * IKeymasterDevice implementations that support device-unique attestation MUST add the
+ * DEVICE_UNIQUE_ATTESTATION tag to device-unique attestations.
+ */
+ DEVICE_UNIQUE_ATTESTATION = TagType:BOOL | 720,
+};
diff --git a/keymaster/4.1/vts/OWNERS b/keymaster/4.1/vts/OWNERS
new file mode 100644
index 0000000..335660d
--- /dev/null
+++ b/keymaster/4.1/vts/OWNERS
@@ -0,0 +1,2 @@
+jdanis@google.com
+swillden@google.com
diff --git a/keymaster/4.1/vts/functional/Android.bp b/keymaster/4.1/vts/functional/Android.bp
new file mode 100644
index 0000000..f5a0c9c
--- /dev/null
+++ b/keymaster/4.1/vts/functional/Android.bp
@@ -0,0 +1,30 @@
+//
+// 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.
+//
+
+cc_test {
+ name: "VtsHalKeymasterV4_1TargetTest",
+ defaults: ["VtsHalTargetTestDefaults"],
+ srcs: [
+ "EarlyBootKeyTest.cpp",
+ ],
+ static_libs: [
+ "android.hardware.keymaster@4.0",
+ "android.hardware.keymaster@4.1",
+ "libkeymaster4support",
+ "libkeymaster4_1support",
+ ],
+ test_suites: ["vts-core"],
+}
diff --git a/keymaster/4.1/vts/functional/EarlyBootKeyTest.cpp b/keymaster/4.1/vts/functional/EarlyBootKeyTest.cpp
new file mode 100644
index 0000000..4a19010
--- /dev/null
+++ b/keymaster/4.1/vts/functional/EarlyBootKeyTest.cpp
@@ -0,0 +1,21 @@
+/*
+ * 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.
+ */
+
+namespace android::hardware::keymaster::V4_1::test {
+
+// TODO(swillden): Put tests here.
+
+} // namespace android::hardware::keymaster::V4_1::test
diff --git a/neuralnetworks/1.3/types.hal b/neuralnetworks/1.3/types.hal
index f959e45..3551d57 100644
--- a/neuralnetworks/1.3/types.hal
+++ b/neuralnetworks/1.3/types.hal
@@ -109,6 +109,7 @@
* * {@link OperandType::TENSOR_FLOAT16} (since HAL version 1.2)
* * {@link OperandType::TENSOR_FLOAT32}
* * {@link OperandType::TENSOR_QUANT8_ASYMM}
+ * * {@link OperandType::TENSOR_QUANT8_ASYMM_SIGNED} (since HAL version 1.3)
*
* Supported tensor rank: up to 4
*
@@ -116,7 +117,8 @@
* * 0: A tensor.
* * 1: A tensor of the same {@link OperandType}, and compatible dimensions
* as input0.
- * For a {@link OperandType::TENSOR_QUANT8_ASYMM} tensor,
+ * For a {@link OperandType::TENSOR_QUANT8_ASYMM} and
+ * {@link OperandType::TENSOR_QUANT8_ASYMM_SIGNED} tensor,
* the scales and zeroPoint can be different from input0 scale and zeroPoint.
* * 2: An {@link OperandType::INT32} scalar, and has to be one of the
* {@link FusedActivationFunc} values. Specifies the activation to
@@ -124,7 +126,8 @@
*
* Outputs:
* * 0: The sum, a tensor of the same {@link OperandType} as input0.
- * For a {@link OperandType::TENSOR_QUANT8_ASYMM} tensor,
+ * For a {@link OperandType::TENSOR_QUANT8_ASYMM} and
+ * {@link OperandType::TENSOR_QUANT8_ASYMM} tensor,
* the scale and zeroPoint can be different from inputs' scale and zeroPoint.
*/
ADD = @1.2::OperationType:ADD,
@@ -146,6 +149,7 @@
* * {@link OperandType::TENSOR_FLOAT16} (since HAL version 1.2)
* * {@link OperandType::TENSOR_FLOAT32}
* * {@link OperandType::TENSOR_QUANT8_ASYMM}
+ * * {@link OperandType::TENSOR_QUANT8_ASYMM_SIGNED} (since HAL version 1.3)
*
* Supported tensor rank: 4, with "NHWC" or "NCHW" data layout.
* With the default data layout NHWC, the data is stored in the order of:
@@ -207,7 +211,8 @@
* Outputs:
* * 0: The output 4-D tensor, of shape
* [batches, out_height, out_width, depth].
- * For a {@link OperandType::TENSOR_QUANT8_ASYMM} tensor,
+ * For a {@link OperandType::TENSOR_QUANT8_ASYMM} and
+ * {@link OperandType::TENSOR_QUANT8_ASYMM_SIGNED} tensor,
* the scale and zeroPoint must be the same as input0.
*/
AVERAGE_POOL_2D = @1.2::OperationType:AVERAGE_POOL_2D,
@@ -951,6 +956,7 @@
* * {@link OperandType::TENSOR_FLOAT16} (since HAL version 1.2)
* * {@link OperandType::TENSOR_FLOAT32}
* * {@link OperandType::TENSOR_QUANT8_ASYMM}
+ * * {@link OperandType::TENSOR_QUANT8_ASYMM_SIGNED} (since HAL version 1.3)
*
* Supported tensor rank: up to 4.
*
@@ -962,6 +968,8 @@
* * 0: The output tensor of same shape as input0.
* For {@link OperandType::TENSOR_QUANT8_ASYMM},
* the scale must be 1.f / 256 and the zeroPoint must be 0.
+ * For {@link OperandType::TENSOR_QUANT8_ASYMM_SIGNED},
+ * the scale must be 1.f / 256 and the zeroPoint must be -128.
*/
LOGISTIC = @1.2::OperationType:LOGISTIC,
@@ -1256,6 +1264,7 @@
* * {@link OperandType::TENSOR_FLOAT16} (since HAL version 1.2)
* * {@link OperandType::TENSOR_FLOAT32}
* * {@link OperandType::TENSOR_QUANT8_ASYMM}
+ * * {@link OperandType::TENSOR_QUANT8_ASYMM_SIGNED} (since HAL version 1.3)
*
* Supported tensor rank: 4, with "NHWC" or "NCHW" data layout.
* With the default data layout NHWC, the data is stored in the order of:
@@ -1317,7 +1326,8 @@
* Outputs:
* * 0: The output 4-D tensor, of shape
* [batches, out_height, out_width, depth].
- * For a {@link OperandType::TENSOR_QUANT8_ASYMM} tensor,
+ * For a {@link OperandType::TENSOR_QUANT8_ASYMM} and
+ * {@link OperandType::TENSOR_QUANT8_ASYMM_SIGNED} tensor,
* the scale and zeroPoint must be the same as input0.
*/
MAX_POOL_2D = @1.2::OperationType:MAX_POOL_2D,
@@ -1345,6 +1355,7 @@
* * {@link OperandType::TENSOR_FLOAT16} (since HAL version 1.2)
* * {@link OperandType::TENSOR_FLOAT32}
* * {@link OperandType::TENSOR_QUANT8_ASYMM}
+ * * {@link OperandType::TENSOR_QUANT8_ASYMM_SIGNED} (since HAL version 1.3)
*
* Supported tensor rank: up to 4
*
@@ -1358,7 +1369,8 @@
*
* Outputs:
* * 0: The product, a tensor of the same {@link OperandType} as input0.
- * For output tensor of {@link OperandType::TENSOR_QUANT8_ASYMM},
+ * For output tensor of {@link OperandType::TENSOR_QUANT8_ASYMM}
+ * and {@link OperandType::TENSOR_QUANT8_ASYMM_SIGNED},
* the following condition must be satisfied:
* output_scale > input1_scale * input2_scale.
*/
@@ -1375,6 +1387,7 @@
* * {@link OperandType::TENSOR_FLOAT16} (since HAL version 1.2)
* * {@link OperandType::TENSOR_FLOAT32}
* * {@link OperandType::TENSOR_QUANT8_ASYMM}
+ * * {@link OperandType::TENSOR_QUANT8_ASYMM_SIGNED} (since HAL version 1.3)
*
* Supported tensor rank: up to 4.
*
@@ -1384,7 +1397,8 @@
*
* Outputs:
* * 0: The output tensor of same shape as input0.
- * For a {@link OperandType::TENSOR_QUANT8_ASYMM} tensor,
+ * For a {@link OperandType::TENSOR_QUANT8_ASYMM} and
+ * {@link OperandType::TENSOR_QUANT8_ASYMM_SIGNED} tensor,
* the scale and zeroPoint must be the same as input0.
*/
RELU = @1.2::OperationType:RELU,
@@ -1400,6 +1414,7 @@
* * {@link OperandType::TENSOR_FLOAT16} (since HAL version 1.2)
* * {@link OperandType::TENSOR_FLOAT32}
* * {@link OperandType::TENSOR_QUANT8_ASYMM}
+ * * {@link OperandType::TENSOR_QUANT8_ASYMM_SIGNED} (since HAL version 1.3)
*
* Supported tensor rank: up to 4.
*
@@ -1409,7 +1424,8 @@
*
* Outputs:
* * 0: The output tensor of the same shape as input0.
- * For a {@link OperandType::TENSOR_QUANT8_ASYMM} tensor,
+ * For a {@link OperandType::TENSOR_QUANT8_ASYMM} and
+ * {@link OperandType::TENSOR_QUANT8_ASYMM_SIGNED} tensor,
* the scale and zeroPoint must be the same as input0.
*/
RELU1 = @1.2::OperationType:RELU1,
@@ -1425,6 +1441,7 @@
* * {@link OperandType::TENSOR_FLOAT16} (since HAL version 1.2)
* * {@link OperandType::TENSOR_FLOAT32}
* * {@link OperandType::TENSOR_QUANT8_ASYMM}
+ * * {@link OperandType::TENSOR_QUANT8_ASYMM_SIGNED} (since HAL version 1.3)
*
* Supported tensor rank: up to 4.
*
@@ -1434,7 +1451,8 @@
*
* Outputs:
* * 0: The output tensor of same shape as input0.
- * For a {@link OperandType::TENSOR_QUANT8_ASYMM} tensor,
+ * For a {@link OperandType::TENSOR_QUANT8_ASYMM} and
+ * {@link OperandType::TENSOR_QUANT8_ASYMM_SIGNED} tensor,
* the scale and zeroPoint must be the same as input0.
*/
RELU6 = @1.2::OperationType:RELU6,
@@ -1755,6 +1773,7 @@
* * {@link OperandType::TENSOR_FLOAT16} (since HAL version 1.2)
* * {@link OperandType::TENSOR_FLOAT32}
* * {@link OperandType::TENSOR_QUANT8_ASYMM} (since HAL version 1.2)
+ * * {@link OperandType::TENSOR_QUANT8_ASYMM_SIGNED} (since HAL version 1.3)
*
* Supported tensor rank: up to 4.
*
@@ -1766,6 +1785,8 @@
* * 0: The output tensor of same shape as input0.
* For {@link OperandType::TENSOR_QUANT8_ASYMM},
* the scale must be 1.f / 128 and the zeroPoint must be 128.
+ * For {@link OperandType::TENSOR_QUANT8_ASYMM_SIGNED},
+ * the scale must be 1.f / 128 and the zeroPoint must be 0.
*/
TANH = @1.2::OperationType:TANH,
@@ -2083,6 +2104,7 @@
* * {@link OperandType::TENSOR_FLOAT16} (since HAL version 1.2)
* * {@link OperandType::TENSOR_FLOAT32}
* * {@link OperandType::TENSOR_QUANT8_ASYMM} (since HAL version 1.2)
+ * * {@link OperandType::TENSOR_QUANT8_ASYMM_SIGNED} (since HAL version 1.3)
*
* Supported tensor rank: up to 4
*
@@ -2096,7 +2118,8 @@
*
* Outputs:
* * 0: A tensor of the same {@link OperandType} as input0.
- * For a {@link OperandType::TENSOR_QUANT8_ASYMM} tensor,
+ * For a {@link OperandType::TENSOR_QUANT8_ASYMM} and
+ * {@link OperandType::TENSOR_QUANT8_ASYMM_SIGNED} tensor,
* the scale and zeroPoint can be different from inputs' scale and zeroPoint.
*/
SUB = @1.2::OperationType:SUB,
diff --git a/radio/config/1.0/vts/functional/radio_config_hidl_hal_utils.h b/radio/config/1.0/vts/functional/radio_config_hidl_hal_utils.h
index 6bc1b65..2722afe 100644
--- a/radio/config/1.0/vts/functional/radio_config_hidl_hal_utils.h
+++ b/radio/config/1.0/vts/functional/radio_config_hidl_hal_utils.h
@@ -27,6 +27,7 @@
#include <gtest/gtest.h>
#include <hidl/GtestPrinter.h>
#include <hidl/ServiceManagement.h>
+#include <log/log.h>
#include "vts_test_util.h"
diff --git a/radio/config/1.1/vts/functional/radio_config_hidl_hal_utils.h b/radio/config/1.1/vts/functional/radio_config_hidl_hal_utils.h
index e9951dc..4cdeb06 100644
--- a/radio/config/1.1/vts/functional/radio_config_hidl_hal_utils.h
+++ b/radio/config/1.1/vts/functional/radio_config_hidl_hal_utils.h
@@ -26,6 +26,7 @@
#include <gtest/gtest.h>
#include <hidl/GtestPrinter.h>
#include <hidl/ServiceManagement.h>
+#include <log/log.h>
#include "vts_test_util.h"
diff --git a/radio/config/1.2/vts/functional/radio_config_hidl_hal_utils.h b/radio/config/1.2/vts/functional/radio_config_hidl_hal_utils.h
index e9cbcbd..ba3f02e 100644
--- a/radio/config/1.2/vts/functional/radio_config_hidl_hal_utils.h
+++ b/radio/config/1.2/vts/functional/radio_config_hidl_hal_utils.h
@@ -28,6 +28,7 @@
#include <gtest/gtest.h>
#include <hidl/GtestPrinter.h>
#include <hidl/ServiceManagement.h>
+#include <log/log.h>
#include "vts_test_util.h"
@@ -98,20 +99,6 @@
const ::android::hardware::hidl_vec<SimSlotStatus>& slotStatus);
};
-// Test environment for Radio HIDL HAL.
-class RadioConfigHidlEnvironment : public ::testing::VtsHalHidlTargetTestEnvBase {
- public:
- // get the test environment singleton
- static RadioConfigHidlEnvironment* Instance() {
- static RadioConfigHidlEnvironment* instance = new RadioConfigHidlEnvironment;
- return instance;
- }
- virtual void registerTestServices() override { registerTestService<IRadioConfig>(); }
-
- private:
- RadioConfigHidlEnvironment() {}
-};
-
// The main test class for Radio config HIDL.
class RadioConfigHidlTest : public ::testing::TestWithParam<std::string> {
protected:
diff --git a/tests/memory/2.0/Android.bp b/tests/memory/2.0/Android.bp
new file mode 100644
index 0000000..5166652
--- /dev/null
+++ b/tests/memory/2.0/Android.bp
@@ -0,0 +1,12 @@
+hidl_interface {
+ name: "android.hardware.tests.memory@2.0",
+ root: "android.hardware",
+ srcs: [
+ "IMemoryInterface.hal",
+ "types.hal",
+ ],
+ interfaces: [
+ "android.hidl.base@1.0",
+ ],
+ gen_java: true,
+}
diff --git a/tests/memory/2.0/IMemoryInterface.hal b/tests/memory/2.0/IMemoryInterface.hal
new file mode 100644
index 0000000..2c824bf
--- /dev/null
+++ b/tests/memory/2.0/IMemoryInterface.hal
@@ -0,0 +1,12 @@
+package android.hardware.tests.memory@2.0;
+
+interface IMemoryInterface {
+ // Flips all the bits in the given memory buffer.
+ bitwiseNot(memory mem);
+ // Returns a read-only buffer of size 8, containing the bytes 0..7.
+ getTestMem() generates(memory mem);
+ // Given two memory regions of the same size, returns two memory fields of
+ // equal size, the first contains the byte-wise sum and the other the byte-
+ // wise difference.
+ getSumDiff(TwoMemory in) generates(TwoMemory out);
+};
diff --git a/tests/memory/2.0/types.hal b/tests/memory/2.0/types.hal
new file mode 100644
index 0000000..9ec357b
--- /dev/null
+++ b/tests/memory/2.0/types.hal
@@ -0,0 +1,6 @@
+package android.hardware.tests.memory@2.0;
+
+struct TwoMemory {
+ memory mem1;
+ memory mem2;
+};
diff --git a/vibrator/aidl/Android.bp b/vibrator/aidl/Android.bp
index cd5439f..e26041a 100644
--- a/vibrator/aidl/Android.bp
+++ b/vibrator/aidl/Android.bp
@@ -5,5 +5,11 @@
"android/hardware/vibrator/*.aidl",
],
stability: "vintf",
+ backend: {
+ ndk: {
+ vndk: {
+ enabled: true,
+ },
+ },
+ },
}
-
diff --git a/vibrator/aidl/android/hardware/vibrator/CompositeEffect.aidl b/vibrator/aidl/android/hardware/vibrator/CompositeEffect.aidl
new file mode 100644
index 0000000..84556b5
--- /dev/null
+++ b/vibrator/aidl/android/hardware/vibrator/CompositeEffect.aidl
@@ -0,0 +1,28 @@
+/*
+ * 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.
+ */
+
+package android.hardware.vibrator;
+
+import android.hardware.vibrator.CompositePrimitive;
+
+@VintfStability
+parcelable CompositeEffect {
+ /* Period of silence preceding primitive. */
+ int delayMs;
+ CompositePrimitive primitive;
+ /* 0.0 (exclusive) - 1.0 (inclusive) */
+ float scale;
+}
diff --git a/vibrator/aidl/android/hardware/vibrator/CompositePrimitive.aidl b/vibrator/aidl/android/hardware/vibrator/CompositePrimitive.aidl
new file mode 100644
index 0000000..2a9d0be
--- /dev/null
+++ b/vibrator/aidl/android/hardware/vibrator/CompositePrimitive.aidl
@@ -0,0 +1,29 @@
+/*
+ * 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.
+ */
+
+package android.hardware.vibrator;
+
+@VintfStability
+@Backing(type="int")
+enum CompositePrimitive {
+ NOOP,
+ CLICK,
+ THUD,
+ SPIN,
+ QUICK_RISE,
+ SLOW_RISE,
+ QUICK_FALL,
+}
diff --git a/vibrator/aidl/android/hardware/vibrator/IVibrator.aidl b/vibrator/aidl/android/hardware/vibrator/IVibrator.aidl
index 8c4fd05..ebf5faa 100644
--- a/vibrator/aidl/android/hardware/vibrator/IVibrator.aidl
+++ b/vibrator/aidl/android/hardware/vibrator/IVibrator.aidl
@@ -19,6 +19,8 @@
import android.hardware.vibrator.IVibratorCallback;
import android.hardware.vibrator.Effect;
import android.hardware.vibrator.EffectStrength;
+import android.hardware.vibrator.CompositeEffect;
+import android.hardware.vibrator.CompositePrimitive;
@VintfStability
interface IVibrator {
@@ -42,6 +44,10 @@
* Whether setAmplitude is supported (when external control is enabled)
*/
const int CAP_EXTERNAL_AMPLITUDE_CONTROL = 1 << 4;
+ /**
+ * Whether compose is supported.
+ */
+ const int CAP_COMPOSE_EFFECTS = 1 << 5;
/**
* Determine capabilities of the vibrator HAL (CAP_* mask)
@@ -107,11 +113,10 @@
* CAP_EXTERNAL_AMPLITUDE_CONTROL.
*
* @param amplitude The unitless force setting. Note that this number must
- * be between 1 and 255, inclusive. If the motor does not
- * have exactly 255 steps, it must do it's best to map it
- * onto the number of steps it does have.
+ * be between 0.0 (exclusive) and 1.0 (inclusive). It must
+ * do it's best to map it onto the number of steps it does have.
*/
- void setAmplitude(in int amplitude);
+ void setAmplitude(in float amplitude);
/**
* Enables/disables control override of vibrator to audio.
@@ -128,4 +133,36 @@
* @param enabled Whether external control should be enabled or disabled.
*/
void setExternalControl(in boolean enabled);
+
+ /**
+ * Retrieve composition delay limit.
+ *
+ * Support is reflected in getCapabilities (CAP_COMPOSE_EFFECTS).
+ *
+ * @return Maximum delay for a single CompositeEffect[] entry.
+ */
+ int getCompositionDelayMax();
+
+ /**
+ * Retrieve composition size limit.
+ *
+ * Support is reflected in getCapabilities (CAP_COMPOSE_EFFECTS).
+ *
+ * @return Maximum number of entries in CompositeEffect[].
+ * @param maxDelayMs Maximum delay for a single CompositeEffect[] entry.
+ */
+ int getCompositionSizeMax();
+
+ /**
+ * Fire off a string of effect primitives, combined to perform richer effects.
+ *
+ * Support is reflected in getCapabilities (CAP_COMPOSE_EFFECTS).
+ *
+ * Doing this operation while the vibrator is already on is undefined behavior. Clients should
+ * explicitly call off.
+ *
+ * @param composite Array of composition parameters.
+ */
+ void compose(in CompositeEffect[] composite, in IVibratorCallback callback);
+
}
diff --git a/vibrator/aidl/default/Vibrator.cpp b/vibrator/aidl/default/Vibrator.cpp
index 09cd234..a77c49a 100644
--- a/vibrator/aidl/default/Vibrator.cpp
+++ b/vibrator/aidl/default/Vibrator.cpp
@@ -24,11 +24,14 @@
namespace hardware {
namespace vibrator {
+static constexpr int32_t kComposeDelayMaxMs = 1000;
+static constexpr int32_t kComposeSizeMax = 256;
+
ndk::ScopedAStatus Vibrator::getCapabilities(int32_t* _aidl_return) {
LOG(INFO) << "Vibrator reporting capabilities";
*_aidl_return = IVibrator::CAP_ON_CALLBACK | IVibrator::CAP_PERFORM_CALLBACK |
IVibrator::CAP_AMPLITUDE_CONTROL | IVibrator::CAP_EXTERNAL_CONTROL |
- IVibrator::CAP_EXTERNAL_AMPLITUDE_CONTROL;
+ IVibrator::CAP_EXTERNAL_AMPLITUDE_CONTROL | IVibrator::CAP_COMPOSE_EFFECTS;
return ndk::ScopedAStatus::ok();
}
@@ -84,9 +87,9 @@
return ndk::ScopedAStatus::ok();
}
-ndk::ScopedAStatus Vibrator::setAmplitude(int32_t amplitude) {
+ndk::ScopedAStatus Vibrator::setAmplitude(float amplitude) {
LOG(INFO) << "Vibrator set amplitude: " << amplitude;
- if (amplitude <= 0 || amplitude > 255) {
+ if (amplitude <= 0.0f || amplitude > 1.0f) {
return ndk::ScopedAStatus(AStatus_fromExceptionCode(EX_ILLEGAL_ARGUMENT));
}
return ndk::ScopedAStatus::ok();
@@ -97,6 +100,55 @@
return ndk::ScopedAStatus::ok();
}
+ndk::ScopedAStatus Vibrator::getCompositionDelayMax(int32_t* maxDelayMs) {
+ *maxDelayMs = kComposeDelayMaxMs;
+ return ndk::ScopedAStatus::ok();
+}
+
+ndk::ScopedAStatus Vibrator::getCompositionSizeMax(int32_t* maxSize) {
+ *maxSize = kComposeSizeMax;
+ return ndk::ScopedAStatus::ok();
+}
+
+ndk::ScopedAStatus Vibrator::compose(const std::vector<CompositeEffect>& composite,
+ const std::shared_ptr<IVibratorCallback>& callback) {
+ if (composite.size() > kComposeSizeMax) {
+ return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
+ }
+
+ for (auto& e : composite) {
+ if (e.delayMs > kComposeDelayMaxMs) {
+ return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
+ }
+ if (e.scale <= 0.0f || e.scale > 1.0f) {
+ return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
+ }
+ if (e.primitive < CompositePrimitive::NOOP ||
+ e.primitive > CompositePrimitive::QUICK_FALL) {
+ return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
+ }
+ }
+
+ std::thread([=] {
+ LOG(INFO) << "Starting compose on another thread";
+
+ for (auto& e : composite) {
+ if (e.delayMs) {
+ usleep(e.delayMs * 1000);
+ }
+ LOG(INFO) << "triggering primitive " << static_cast<int>(e.primitive) << " @ scale "
+ << e.scale;
+ }
+
+ if (callback != nullptr) {
+ LOG(INFO) << "Notifying perform complete";
+ callback->onComplete();
+ }
+ }).detach();
+
+ return ndk::ScopedAStatus::ok();
+}
+
} // namespace vibrator
} // namespace hardware
} // namespace android
diff --git a/vibrator/aidl/default/include/vibrator-impl/Vibrator.h b/vibrator/aidl/default/include/vibrator-impl/Vibrator.h
index 14e7292..817ec80 100644
--- a/vibrator/aidl/default/include/vibrator-impl/Vibrator.h
+++ b/vibrator/aidl/default/include/vibrator-impl/Vibrator.h
@@ -32,8 +32,12 @@
const std::shared_ptr<IVibratorCallback>& callback,
int32_t* _aidl_return) override;
ndk::ScopedAStatus getSupportedEffects(std::vector<Effect>* _aidl_return) override;
- ndk::ScopedAStatus setAmplitude(int32_t amplitude) override;
+ ndk::ScopedAStatus setAmplitude(float amplitude) override;
ndk::ScopedAStatus setExternalControl(bool enabled) override;
+ ndk::ScopedAStatus getCompositionDelayMax(int32_t* maxDelayMs);
+ ndk::ScopedAStatus getCompositionSizeMax(int32_t* maxSize);
+ ndk::ScopedAStatus compose(const std::vector<CompositeEffect>& composite,
+ const std::shared_ptr<IVibratorCallback>& callback) override;
};
} // namespace vibrator
diff --git a/vibrator/aidl/vts/VtsHalVibratorTargetTest.cpp b/vibrator/aidl/vts/VtsHalVibratorTargetTest.cpp
index b6aa9e2..5c6120b 100644
--- a/vibrator/aidl/vts/VtsHalVibratorTargetTest.cpp
+++ b/vibrator/aidl/vts/VtsHalVibratorTargetTest.cpp
@@ -28,6 +28,8 @@
using android::String16;
using android::binder::Status;
using android::hardware::vibrator::BnVibratorCallback;
+using android::hardware::vibrator::CompositeEffect;
+using android::hardware::vibrator::CompositePrimitive;
using android::hardware::vibrator::Effect;
using android::hardware::vibrator::EffectStrength;
using android::hardware::vibrator::IVibrator;
@@ -55,6 +57,20 @@
static_cast<EffectStrength>(static_cast<int8_t>(kEffectStrengths.back()) + 1),
};
+// TODO(b/143992652): autogenerate
+const std::vector<CompositePrimitive> kCompositePrimitives = {
+ CompositePrimitive::NOOP, CompositePrimitive::CLICK,
+ CompositePrimitive::THUD, CompositePrimitive::SPIN,
+ CompositePrimitive::QUICK_RISE, CompositePrimitive::SLOW_RISE,
+ CompositePrimitive::QUICK_FALL,
+};
+// TODO(b/143992652): autogenerate
+
+const std::vector<CompositePrimitive> kInvalidPrimitives = {
+ static_cast<CompositePrimitive>(static_cast<int32_t>(kCompositePrimitives.front()) - 1),
+ static_cast<CompositePrimitive>(static_cast<int32_t>(kCompositePrimitives.back()) + 1),
+};
+
class CompletionCallback : public BnVibratorCallback {
public:
CompletionCallback(const std::function<void()>& callback) : mCallback(callback) {}
@@ -201,11 +217,11 @@
TEST_P(VibratorAidl, ChangeVibrationAmplitude) {
if (capabilities & IVibrator::CAP_AMPLITUDE_CONTROL) {
- EXPECT_TRUE(vibrator->setAmplitude(1).isOk());
+ EXPECT_EQ(Status::EX_NONE, vibrator->setAmplitude(0.1f).exceptionCode());
EXPECT_TRUE(vibrator->on(2000, nullptr /*callback*/).isOk());
- EXPECT_TRUE(vibrator->setAmplitude(128).isOk());
+ EXPECT_EQ(Status::EX_NONE, vibrator->setAmplitude(0.5f).exceptionCode());
sleep(1);
- EXPECT_TRUE(vibrator->setAmplitude(255).isOk());
+ EXPECT_EQ(Status::EX_NONE, vibrator->setAmplitude(1.0f).exceptionCode());
sleep(1);
}
}
@@ -214,7 +230,7 @@
if (capabilities & IVibrator::CAP_AMPLITUDE_CONTROL) {
EXPECT_EQ(Status::EX_ILLEGAL_ARGUMENT, vibrator->setAmplitude(-1).exceptionCode());
EXPECT_EQ(Status::EX_ILLEGAL_ARGUMENT, vibrator->setAmplitude(0).exceptionCode());
- EXPECT_EQ(Status::EX_ILLEGAL_ARGUMENT, vibrator->setAmplitude(256).exceptionCode());
+ EXPECT_EQ(Status::EX_ILLEGAL_ARGUMENT, vibrator->setAmplitude(1.1).exceptionCode());
}
}
@@ -240,7 +256,7 @@
if (capabilities & IVibrator::CAP_EXTERNAL_CONTROL) {
EXPECT_TRUE(vibrator->setExternalControl(true).isOk());
- Status amplitudeStatus = vibrator->setAmplitude(128);
+ Status amplitudeStatus = vibrator->setAmplitude(0.5);
if (supportsExternalAmplitudeControl) {
EXPECT_TRUE(amplitudeStatus.isOk());
} else {
@@ -259,6 +275,102 @@
}
}
+TEST_P(VibratorAidl, ComposeValidPrimitives) {
+ if (capabilities & IVibrator::CAP_COMPOSE_EFFECTS) {
+ int32_t maxDelay, maxSize;
+
+ EXPECT_EQ(Status::EX_NONE, vibrator->getCompositionDelayMax(&maxDelay).exceptionCode());
+ EXPECT_EQ(Status::EX_NONE, vibrator->getCompositionSizeMax(&maxSize).exceptionCode());
+
+ std::vector<CompositeEffect> composite;
+
+ for (auto primitive : kCompositePrimitives) {
+ CompositeEffect effect;
+
+ effect.delayMs = std::rand() % (maxDelay + 1);
+ effect.primitive = primitive;
+ effect.scale = static_cast<float>(std::rand()) / RAND_MAX ?: 1.0f;
+ composite.emplace_back(effect);
+
+ if (composite.size() == maxSize) {
+ EXPECT_EQ(Status::EX_NONE, vibrator->compose(composite, nullptr).exceptionCode());
+ composite.clear();
+ vibrator->off();
+ }
+ }
+
+ if (composite.size() != 0) {
+ EXPECT_EQ(Status::EX_NONE, vibrator->compose(composite, nullptr).exceptionCode());
+ vibrator->off();
+ }
+ }
+}
+
+TEST_P(VibratorAidl, ComposeUnsupportedPrimitives) {
+ if (capabilities & IVibrator::CAP_COMPOSE_EFFECTS) {
+ for (auto primitive : kInvalidPrimitives) {
+ std::vector<CompositeEffect> composite(1);
+
+ for (auto& effect : composite) {
+ effect.delayMs = 0;
+ effect.primitive = primitive;
+ effect.scale = 1.0f;
+ }
+ EXPECT_EQ(Status::EX_UNSUPPORTED_OPERATION,
+ vibrator->compose(composite, nullptr).exceptionCode());
+ vibrator->off();
+ }
+ }
+}
+
+TEST_P(VibratorAidl, CompseDelayBoundary) {
+ if (capabilities & IVibrator::CAP_COMPOSE_EFFECTS) {
+ int32_t maxDelay;
+
+ EXPECT_EQ(Status::EX_NONE, vibrator->getCompositionDelayMax(&maxDelay).exceptionCode());
+
+ std::vector<CompositeEffect> composite(1);
+ CompositeEffect effect;
+
+ effect.delayMs = 1;
+ effect.primitive = CompositePrimitive::CLICK;
+ effect.scale = 1.0f;
+
+ std::fill(composite.begin(), composite.end(), effect);
+ EXPECT_EQ(Status::EX_NONE, vibrator->compose(composite, nullptr).exceptionCode());
+
+ effect.delayMs = maxDelay + 1;
+
+ std::fill(composite.begin(), composite.end(), effect);
+ EXPECT_EQ(Status::EX_ILLEGAL_ARGUMENT,
+ vibrator->compose(composite, nullptr).exceptionCode());
+ vibrator->off();
+ }
+}
+
+TEST_P(VibratorAidl, CompseSizeBoundary) {
+ if (capabilities & IVibrator::CAP_COMPOSE_EFFECTS) {
+ int32_t maxSize;
+
+ EXPECT_EQ(Status::EX_NONE, vibrator->getCompositionSizeMax(&maxSize).exceptionCode());
+
+ std::vector<CompositeEffect> composite(maxSize);
+ CompositeEffect effect;
+
+ effect.delayMs = 1;
+ effect.primitive = CompositePrimitive::CLICK;
+ effect.scale = 1.0f;
+
+ std::fill(composite.begin(), composite.end(), effect);
+ EXPECT_EQ(Status::EX_NONE, vibrator->compose(composite, nullptr).exceptionCode());
+
+ composite.emplace_back(effect);
+ EXPECT_EQ(Status::EX_ILLEGAL_ARGUMENT,
+ vibrator->compose(composite, nullptr).exceptionCode());
+ vibrator->off();
+ }
+}
+
INSTANTIATE_TEST_SUITE_P(Vibrator, VibratorAidl,
testing::ValuesIn(android::getAidlHalInstanceNames(IVibrator::descriptor)),
android::PrintInstanceNameToString);