Merge "uwb(hal): Add doc for the vendor command versioning scheme"
diff --git a/automotive/evs/1.1/vts/functional/VtsHalEvsV1_1TargetTest.cpp b/automotive/evs/1.1/vts/functional/VtsHalEvsV1_1TargetTest.cpp
index e1b9729..c33a2e7 100644
--- a/automotive/evs/1.1/vts/functional/VtsHalEvsV1_1TargetTest.cpp
+++ b/automotive/evs/1.1/vts/functional/VtsHalEvsV1_1TargetTest.cpp
@@ -79,19 +79,24 @@
using IEvsDisplay_1_0 = ::android::hardware::automotive::evs::V1_0::IEvsDisplay;
using IEvsDisplay_1_1 = ::android::hardware::automotive::evs::V1_1::IEvsDisplay;
+namespace {
+
/*
* Plese note that this is different from what is defined in
* libhardware/modules/camera/3_4/metadata/types.h; this has one additional
* field to store a framerate.
*/
-const size_t kStreamCfgSz = 5;
typedef struct {
+ int32_t id;
int32_t width;
int32_t height;
int32_t format;
int32_t direction;
int32_t framerate;
} RawStreamConfig;
+constexpr const size_t kStreamCfgSz = sizeof(RawStreamConfig) / sizeof(int32_t);
+
+} // anonymous namespace
// The main test class for EVS
@@ -236,6 +241,28 @@
return physicalCameras;
}
+ Stream getFirstStreamConfiguration(camera_metadata_t* metadata) {
+ Stream targetCfg = {};
+ camera_metadata_entry_t streamCfgs;
+ if (!find_camera_metadata_entry(metadata,
+ ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS,
+ &streamCfgs)) {
+ // Stream configurations are found in metadata
+ RawStreamConfig *ptr = reinterpret_cast<RawStreamConfig *>(streamCfgs.data.i32);
+ for (unsigned offset = 0; offset < streamCfgs.count; offset += kStreamCfgSz) {
+ if (ptr->direction == ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS_OUTPUT &&
+ ptr->format == HAL_PIXEL_FORMAT_RGBA_8888) {
+ targetCfg.width = ptr->width;
+ targetCfg.height = ptr->height;
+ targetCfg.format = static_cast<PixelFormat>(ptr->format);
+ break;
+ }
+ ++ptr;
+ }
+ }
+
+ return targetCfg;
+ }
sp<IEvsEnumerator> pEnumerator; // Every test needs access to the service
std::vector<CameraDesc> cameraInfo; // Empty unless/until loadCameraList() is called
@@ -265,10 +292,6 @@
// Get the camera list
loadCameraList();
- // Using null stream configuration makes EVS uses the default resolution and
- // output format.
- Stream nullCfg = {};
-
// Open and close each camera twice
for (auto&& cam: cameraInfo) {
bool isLogicalCam = false;
@@ -278,8 +301,14 @@
continue;
}
+ // Read a target resolution from the metadata
+ Stream targetCfg =
+ getFirstStreamConfiguration(reinterpret_cast<camera_metadata_t*>(cam.metadata.data()));
+ ASSERT_GT(targetCfg.width, 0);
+ ASSERT_GT(targetCfg.height, 0);
+
for (int pass = 0; pass < 2; pass++) {
- sp<IEvsCamera_1_1> pCam = pEnumerator->openCamera_1_1(cam.v1.cameraId, nullCfg);
+ sp<IEvsCamera_1_1> pCam = pEnumerator->openCamera_1_1(cam.v1.cameraId, targetCfg);
ASSERT_NE(pCam, nullptr);
for (auto&& devName : devices) {
@@ -343,10 +372,6 @@
// Get the camera list
loadCameraList();
- // Using null stream configuration makes EVS uses the default resolution and
- // output format.
- Stream nullCfg = {};
-
// Open and close each camera twice
for (auto&& cam: cameraInfo) {
bool isLogicalCam = false;
@@ -356,10 +381,14 @@
continue;
}
+ // Read a target resolution from the metadata
+ Stream targetCfg =
+ getFirstStreamConfiguration(reinterpret_cast<camera_metadata_t*>(cam.metadata.data()));
+ ASSERT_GT(targetCfg.width, 0);
+ ASSERT_GT(targetCfg.height, 0);
+
activeCameras.clear();
- sp<IEvsCamera_1_1> pCam =
- IEvsCamera_1_1::castFrom(pEnumerator->openCamera_1_1(cam.v1.cameraId, nullCfg))
- .withDefault(nullptr);
+ sp<IEvsCamera_1_1> pCam = pEnumerator->openCamera_1_1(cam.v1.cameraId, targetCfg);
ASSERT_NE(pCam, nullptr);
// Store a camera handle for a clean-up
@@ -372,9 +401,7 @@
}
);
- sp<IEvsCamera_1_1> pCam2 =
- IEvsCamera_1_1::castFrom(pEnumerator->openCamera_1_1(cam.v1.cameraId, nullCfg))
- .withDefault(nullptr);
+ sp<IEvsCamera_1_1> pCam2 = pEnumerator->openCamera_1_1(cam.v1.cameraId, targetCfg);
ASSERT_NE(pCam2, nullptr);
// Store a camera handle for a clean-up
@@ -422,10 +449,6 @@
// Get the camera list
loadCameraList();
- // Using null stream configuration makes EVS uses the default resolution and
- // output format.
- Stream nullCfg = {};
-
// Test each reported camera
for (auto&& cam: cameraInfo) {
bool isLogicalCam = false;
@@ -435,9 +458,13 @@
continue;
}
- sp<IEvsCamera_1_1> pCam =
- IEvsCamera_1_1::castFrom(pEnumerator->openCamera_1_1(cam.v1.cameraId, nullCfg))
- .withDefault(nullptr);
+ // Read a target resolution from the metadata
+ Stream targetCfg =
+ getFirstStreamConfiguration(reinterpret_cast<camera_metadata_t*>(cam.metadata.data()));
+ ASSERT_GT(targetCfg.width, 0);
+ ASSERT_GT(targetCfg.height, 0);
+
+ sp<IEvsCamera_1_1> pCam = pEnumerator->openCamera_1_1(cam.v1.cameraId, targetCfg);
ASSERT_NE(pCam, nullptr);
// Store a camera handle for a clean-up
@@ -519,10 +546,6 @@
// Get the camera list
loadCameraList();
- // Using null stream configuration makes EVS uses the default resolution and
- // output format.
- Stream nullCfg = {};
-
// Test each reported camera
for (auto&& cam: cameraInfo) {
bool isLogicalCam = false;
@@ -532,9 +555,13 @@
continue;
}
- sp<IEvsCamera_1_1> pCam =
- IEvsCamera_1_1::castFrom(pEnumerator->openCamera_1_1(cam.v1.cameraId, nullCfg))
- .withDefault(nullptr);
+ // Read a target resolution from the metadata
+ Stream targetCfg =
+ getFirstStreamConfiguration(reinterpret_cast<camera_metadata_t*>(cam.metadata.data()));
+ ASSERT_GT(targetCfg.width, 0);
+ ASSERT_GT(targetCfg.height, 0);
+
+ sp<IEvsCamera_1_1> pCam = pEnumerator->openCamera_1_1(cam.v1.cameraId, targetCfg);
ASSERT_NE(pCam, nullptr);
// Store a camera handle for a clean-up
@@ -601,10 +628,6 @@
// Get the camera list
loadCameraList();
- // Using null stream configuration makes EVS uses the default resolution and
- // output format.
- Stream nullCfg = {};
-
// Request available display IDs
uint8_t targetDisplayId = 0;
pEnumerator->getDisplayIdList([&targetDisplayId](auto ids) {
@@ -642,9 +665,13 @@
continue;
}
- sp<IEvsCamera_1_1> pCam =
- IEvsCamera_1_1::castFrom(pEnumerator->openCamera_1_1(cam.v1.cameraId, nullCfg))
- .withDefault(nullptr);
+ // Read a target resolution from the metadata
+ Stream targetCfg =
+ getFirstStreamConfiguration(reinterpret_cast<camera_metadata_t*>(cam.metadata.data()));
+ ASSERT_GT(targetCfg.width, 0);
+ ASSERT_GT(targetCfg.height, 0);
+
+ sp<IEvsCamera_1_1> pCam = pEnumerator->openCamera_1_1(cam.v1.cameraId, targetCfg);
ASSERT_NE(pCam, nullptr);
// Store a camera handle for a clean-up
@@ -708,24 +735,22 @@
// Get the camera list
loadCameraList();
- // Using null stream configuration makes EVS uses the default resolution and
- // output format.
- Stream nullCfg = {};
-
// Test each reported camera
for (auto&& cam: cameraInfo) {
+ // Read a target resolution from the metadata
+ Stream targetCfg =
+ getFirstStreamConfiguration(reinterpret_cast<camera_metadata_t*>(cam.metadata.data()));
+ ASSERT_GT(targetCfg.width, 0);
+ ASSERT_GT(targetCfg.height, 0);
+
// Create two camera clients.
- sp<IEvsCamera_1_1> pCam0 =
- IEvsCamera_1_1::castFrom(pEnumerator->openCamera_1_1(cam.v1.cameraId, nullCfg))
- .withDefault(nullptr);
+ sp<IEvsCamera_1_1> pCam0 = pEnumerator->openCamera_1_1(cam.v1.cameraId, targetCfg);
ASSERT_NE(pCam0, nullptr);
// Store a camera handle for a clean-up
activeCameras.push_back(pCam0);
- sp<IEvsCamera_1_1> pCam1 =
- IEvsCamera_1_1::castFrom(pEnumerator->openCamera_1_1(cam.v1.cameraId, nullCfg))
- .withDefault(nullptr);
+ sp<IEvsCamera_1_1> pCam1 = pEnumerator->openCamera_1_1(cam.v1.cameraId, targetCfg);
ASSERT_NE(pCam1, nullptr);
// Store a camera handle for a clean-up
@@ -812,10 +837,6 @@
// Get the camera list
loadCameraList();
- // Using null stream configuration makes EVS uses the default resolution and
- // output format.
- Stream nullCfg = {};
-
// Test each reported camera
Return<EvsResult> result = EvsResult::OK;
for (auto&& cam: cameraInfo) {
@@ -828,10 +849,14 @@
continue;
}
+ // Read a target resolution from the metadata
+ Stream targetCfg =
+ getFirstStreamConfiguration(reinterpret_cast<camera_metadata_t*>(cam.metadata.data()));
+ ASSERT_GT(targetCfg.width, 0);
+ ASSERT_GT(targetCfg.height, 0);
+
// Create a camera client
- sp<IEvsCamera_1_1> pCam =
- IEvsCamera_1_1::castFrom(pEnumerator->openCamera_1_1(cam.v1.cameraId, nullCfg))
- .withDefault(nullptr);
+ sp<IEvsCamera_1_1> pCam = pEnumerator->openCamera_1_1(cam.v1.cameraId, targetCfg);
ASSERT_NE(pCam, nullptr);
// Store a camera
@@ -961,10 +986,6 @@
// Get the camera list
loadCameraList();
- // Using null stream configuration makes EVS uses the default resolution and
- // output format.
- Stream nullCfg = {};
-
// Test each reported camera
for (auto&& cam: cameraInfo) {
bool isLogicalCam = false;
@@ -976,18 +997,20 @@
continue;
}
+ // Read a target resolution from the metadata
+ Stream targetCfg =
+ getFirstStreamConfiguration(reinterpret_cast<camera_metadata_t*>(cam.metadata.data()));
+ ASSERT_GT(targetCfg.width, 0);
+ ASSERT_GT(targetCfg.height, 0);
+
// Create two camera clients.
- sp<IEvsCamera_1_1> pCamPrimary =
- IEvsCamera_1_1::castFrom(pEnumerator->openCamera_1_1(cam.v1.cameraId, nullCfg))
- .withDefault(nullptr);
+ sp<IEvsCamera_1_1> pCamPrimary = pEnumerator->openCamera_1_1(cam.v1.cameraId, targetCfg);
ASSERT_NE(pCamPrimary, nullptr);
// Store a camera handle for a clean-up
activeCameras.push_back(pCamPrimary);
- sp<IEvsCamera_1_1> pCamSecondary =
- IEvsCamera_1_1::castFrom(pEnumerator->openCamera_1_1(cam.v1.cameraId, nullCfg))
- .withDefault(nullptr);
+ sp<IEvsCamera_1_1> pCamSecondary = pEnumerator->openCamera_1_1(cam.v1.cameraId, targetCfg);
ASSERT_NE(pCamSecondary, nullptr);
// Store a camera handle for a clean-up
@@ -1142,10 +1165,6 @@
// Get the camera list
loadCameraList();
- // Using null stream configuration makes EVS uses the default resolution and
- // output format.
- Stream nullCfg = {};
-
// Test each reported camera
for (auto&& cam: cameraInfo) {
bool isLogicalCam = false;
@@ -1157,18 +1176,20 @@
continue;
}
+ // Read a target resolution from the metadata
+ Stream targetCfg =
+ getFirstStreamConfiguration(reinterpret_cast<camera_metadata_t*>(cam.metadata.data()));
+ ASSERT_GT(targetCfg.width, 0);
+ ASSERT_GT(targetCfg.height, 0);
+
// Create two camera clients.
- sp<IEvsCamera_1_1> pCamPrimary =
- IEvsCamera_1_1::castFrom(pEnumerator->openCamera_1_1(cam.v1.cameraId, nullCfg))
- .withDefault(nullptr);
+ sp<IEvsCamera_1_1> pCamPrimary = pEnumerator->openCamera_1_1(cam.v1.cameraId, targetCfg);
ASSERT_NE(pCamPrimary, nullptr);
// Store a camera handle for a clean-up
activeCameras.push_back(pCamPrimary);
- sp<IEvsCamera_1_1> pCamSecondary =
- IEvsCamera_1_1::castFrom(pEnumerator->openCamera_1_1(cam.v1.cameraId, nullCfg))
- .withDefault(nullptr);
+ sp<IEvsCamera_1_1> pCamSecondary = pEnumerator->openCamera_1_1(cam.v1.cameraId, targetCfg);
ASSERT_NE(pCamSecondary, nullptr);
// Store a camera handle for a clean-up
@@ -1615,28 +1636,26 @@
// Get the camera list
loadCameraList();
- // Using null stream configuration makes EVS uses the default resolution and
- // output format.
- Stream nullCfg = {};
-
// Request exclusive access to the EVS display
sp<IEvsDisplay_1_0> pDisplay = pEnumerator->openDisplay();
ASSERT_NE(pDisplay, nullptr);
// Test each reported camera
for (auto&& cam: cameraInfo) {
+ // Read a target resolution from the metadata
+ Stream targetCfg =
+ getFirstStreamConfiguration(reinterpret_cast<camera_metadata_t*>(cam.metadata.data()));
+ ASSERT_GT(targetCfg.width, 0);
+ ASSERT_GT(targetCfg.height, 0);
+
// Create two clients
- sp<IEvsCamera_1_1> pCam0 =
- IEvsCamera_1_1::castFrom(pEnumerator->openCamera_1_1(cam.v1.cameraId, nullCfg))
- .withDefault(nullptr);
+ sp<IEvsCamera_1_1> pCam0 = pEnumerator->openCamera_1_1(cam.v1.cameraId, targetCfg);
ASSERT_NE(pCam0, nullptr);
// Store a camera handle for a clean-up
activeCameras.push_back(pCam0);
- sp<IEvsCamera_1_1> pCam1 =
- IEvsCamera_1_1::castFrom(pEnumerator->openCamera_1_1(cam.v1.cameraId, nullCfg))
- .withDefault(nullptr);
+ sp<IEvsCamera_1_1> pCam1 = pEnumerator->openCamera_1_1(cam.v1.cameraId, targetCfg);
ASSERT_NE(pCam1, nullptr);
// Store a camera handle for a clean-up
@@ -2001,7 +2020,7 @@
&streamCfgs)) {
// Stream configurations are found in metadata
RawStreamConfig *ptr = reinterpret_cast<RawStreamConfig *>(streamCfgs.data.i32);
- for (unsigned idx = 0; idx < streamCfgs.count; idx += kStreamCfgSz) {
+ for (unsigned offset = 0; offset < streamCfgs.count; offset += kStreamCfgSz) {
if (ptr->direction == ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS_OUTPUT &&
ptr->format == HAL_PIXEL_FORMAT_RGBA_8888) {
@@ -2026,9 +2045,7 @@
continue;
}
- sp<IEvsCamera_1_1> pCam =
- IEvsCamera_1_1::castFrom(pEnumerator->openCamera_1_1(cam.v1.cameraId, targetCfg))
- .withDefault(nullptr);
+ sp<IEvsCamera_1_1> pCam = pEnumerator->openCamera_1_1(cam.v1.cameraId, targetCfg);
ASSERT_NE(pCam, nullptr);
// Store a camera handle for a clean-up
@@ -2106,7 +2123,7 @@
&streamCfgs)) {
// Stream configurations are found in metadata
RawStreamConfig *ptr = reinterpret_cast<RawStreamConfig *>(streamCfgs.data.i32);
- for (unsigned idx = 0; idx < streamCfgs.count; idx += kStreamCfgSz) {
+ for (unsigned offset = 0; offset < streamCfgs.count; offset += kStreamCfgSz) {
if (ptr->direction == ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS_OUTPUT &&
ptr->format == HAL_PIXEL_FORMAT_RGBA_8888) {
@@ -2132,9 +2149,7 @@
}
// Create the first camera client with a selected stream configuration.
- sp<IEvsCamera_1_1> pCam0 =
- IEvsCamera_1_1::castFrom(pEnumerator->openCamera_1_1(cam.v1.cameraId, targetCfg))
- .withDefault(nullptr);
+ sp<IEvsCamera_1_1> pCam0 = pEnumerator->openCamera_1_1(cam.v1.cameraId, targetCfg);
ASSERT_NE(pCam0, nullptr);
// Store a camera handle for a clean-up
@@ -2144,9 +2159,7 @@
// configuration.
int32_t id = targetCfg.id;
targetCfg.id += 1; // EVS manager sees only the stream id.
- sp<IEvsCamera_1_1> pCam1 =
- IEvsCamera_1_1::castFrom(pEnumerator->openCamera_1_1(cam.v1.cameraId, targetCfg))
- .withDefault(nullptr);
+ sp<IEvsCamera_1_1> pCam1 = pEnumerator->openCamera_1_1(cam.v1.cameraId, targetCfg);
ASSERT_EQ(pCam1, nullptr);
// Store a camera handle for a clean-up
@@ -2154,9 +2167,7 @@
// Try again with same stream configuration.
targetCfg.id = id;
- pCam1 =
- IEvsCamera_1_1::castFrom(pEnumerator->openCamera_1_1(cam.v1.cameraId, targetCfg))
- .withDefault(nullptr);
+ pCam1 = pEnumerator->openCamera_1_1(cam.v1.cameraId, targetCfg);
ASSERT_NE(pCam1, nullptr);
// Set up per-client frame receiver objects which will fire up its own thread
@@ -2258,52 +2269,23 @@
LOG(INFO) << "Starting CameraStreamExternalBuffering test";
// Arbitrary constant (should be > 1 and not too big)
- static const unsigned int kBuffersToHold = 6;
+ static const unsigned int kBuffersToHold = 3;
// Get the camera list
loadCameraList();
- // Using null stream configuration makes EVS uses the default resolution and
- // output format.
- Stream nullCfg = {};
-
// Acquire the graphics buffer allocator
android::GraphicBufferAllocator& alloc(android::GraphicBufferAllocator::get());
const auto usage =
GRALLOC_USAGE_HW_TEXTURE | GRALLOC_USAGE_SW_READ_RARELY | GRALLOC_USAGE_SW_WRITE_OFTEN;
- const auto format = HAL_PIXEL_FORMAT_RGBA_8888;
- uint32_t width = 640;
- uint32_t height = 360;
- camera_metadata_entry_t streamCfgs;
// Test each reported camera
for (auto&& cam : cameraInfo) {
- bool foundCfg = false;
- if (!find_camera_metadata_entry(reinterpret_cast<camera_metadata_t*>(cam.metadata.data()),
- ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS,
- &streamCfgs)) {
- // Stream configurations are found in metadata
- RawStreamConfig* ptr = reinterpret_cast<RawStreamConfig*>(streamCfgs.data.i32);
-
- LOG(DEBUG) << __LINE__ << " start searching " << streamCfgs.count;
- for (unsigned idx = 0; idx < streamCfgs.count; idx++) {
- LOG(DEBUG) << "ptr->direction= " << ptr->direction
- << " ptr->format= " << ptr->format;
- if (ptr->direction == ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS_OUTPUT &&
- ptr->format == HAL_PIXEL_FORMAT_RGBA_8888) {
- width = ptr->width;
- height = ptr->height;
- foundCfg = true;
- // Always use the 1st available configuration
- break;
- }
- ++ptr;
- }
- }
-
- if (!foundCfg) {
- LOG(INFO) << "No configuration found. Use default stream configurations.";
- }
+ // Read a target resolution from the metadata
+ Stream targetCfg =
+ getFirstStreamConfiguration(reinterpret_cast<camera_metadata_t*>(cam.metadata.data()));
+ ASSERT_GT(targetCfg.width, 0);
+ ASSERT_GT(targetCfg.height, 0);
// Allocate buffers to use
hidl_vec<BufferDesc> buffers;
@@ -2312,8 +2294,11 @@
unsigned pixelsPerLine;
buffer_handle_t memHandle = nullptr;
android::status_t result =
- alloc.allocate(width, height, format, 1, usage, &memHandle, &pixelsPerLine, 0,
- "CameraStreamExternalBufferingTest");
+ alloc.allocate(targetCfg.width, targetCfg.height,
+ (android::PixelFormat)targetCfg.format,
+ /* layerCount = */ 1, usage, &memHandle, &pixelsPerLine,
+ /* graphicBufferId = */ 0,
+ /* requestorName = */ "CameraStreamExternalBufferingTest");
if (result != android::NO_ERROR) {
LOG(ERROR) << __FUNCTION__ << " failed to allocate memory.";
// Release previous allocated buffers
@@ -2325,10 +2310,10 @@
BufferDesc buf;
AHardwareBuffer_Desc* pDesc =
reinterpret_cast<AHardwareBuffer_Desc*>(&buf.buffer.description);
- pDesc->width = width;
- pDesc->height = height;
+ pDesc->width = targetCfg.width;
+ pDesc->height = targetCfg.height;
pDesc->layers = 1;
- pDesc->format = format;
+ pDesc->format = static_cast<uint32_t>(targetCfg.format);
pDesc->usage = usage;
pDesc->stride = pixelsPerLine;
buf.buffer.nativeHandle = memHandle;
@@ -2340,9 +2325,7 @@
bool isLogicalCam = false;
getPhysicalCameraIds(cam.v1.cameraId, isLogicalCam);
- sp<IEvsCamera_1_1> pCam =
- IEvsCamera_1_1::castFrom(pEnumerator->openCamera_1_1(cam.v1.cameraId, nullCfg))
- .withDefault(nullptr);
+ sp<IEvsCamera_1_1> pCam = pEnumerator->openCamera_1_1(cam.v1.cameraId, targetCfg);
ASSERT_NE(pCam, nullptr);
// Store a camera handle for a clean-up
@@ -2362,7 +2345,7 @@
}
EXPECT_EQ(result, EvsResult::OK);
- EXPECT_GE(delta, 0);
+ EXPECT_GE(delta, kBuffersToHold);
// Set up a frame receiver object which will fire up its own thread.
sp<FrameHandler> frameHandler = new FrameHandler(pCam, cam,
@@ -2378,7 +2361,7 @@
sleep(1); // 1 second should be enough for at least 5 frames to be delivered worst case
unsigned framesReceived = 0;
frameHandler->getFramesCounters(&framesReceived, nullptr);
- ASSERT_EQ(kBuffersToHold, framesReceived) << "Stream didn't stall at expected buffer limit";
+ ASSERT_LE(kBuffersToHold, framesReceived) << "Stream didn't stall at expected buffer limit";
// Give back one buffer
@@ -2387,9 +2370,10 @@
// Once we return a buffer, it shouldn't take more than 1/10 second to get a new one
// filled since we require 10fps minimum -- but give a 10% allowance just in case.
+ unsigned framesReceivedAfter = 0;
usleep(110 * kMillisecondsToMicroseconds);
- frameHandler->getFramesCounters(&framesReceived, nullptr);
- EXPECT_EQ(kBuffersToHold+1, framesReceived) << "Stream should've resumed";
+ frameHandler->getFramesCounters(&framesReceivedAfter, nullptr);
+ EXPECT_EQ(framesReceived + 1, framesReceivedAfter) << "Stream should've resumed";
// Even when the camera pointer goes out of scope, the FrameHandler object will
// keep the stream alive unless we tell it to shutdown.
diff --git a/automotive/vehicle/TEST_MAPPING b/automotive/vehicle/TEST_MAPPING
index 4820fd4..9924581 100644
--- a/automotive/vehicle/TEST_MAPPING
+++ b/automotive/vehicle/TEST_MAPPING
@@ -8,6 +8,12 @@
},
{
"name": "VehicleHalVehicleUtilsTest"
+ },
+ {
+ "name": "FakeVehicleHardwareTest"
+ },
+ {
+ "name": "FakeVehicleHalValueGeneratorsTest"
}
]
}
diff --git a/automotive/vehicle/aidl/aidl_api/android.hardware.automotive.vehicle/current/android/hardware/automotive/vehicle/GetValueRequest.aidl b/automotive/vehicle/aidl/aidl_api/android.hardware.automotive.vehicle/current/android/hardware/automotive/vehicle/GetValueRequest.aidl
new file mode 100644
index 0000000..177966b
--- /dev/null
+++ b/automotive/vehicle/aidl/aidl_api/android.hardware.automotive.vehicle/current/android/hardware/automotive/vehicle/GetValueRequest.aidl
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2021 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.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+// the interface (from the latest frozen version), the build system will
+// prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.hardware.automotive.vehicle;
+@VintfStability
+parcelable GetValueRequest {
+ long requestId;
+ android.hardware.automotive.vehicle.VehiclePropValue prop;
+}
diff --git a/automotive/vehicle/aidl/aidl_api/android.hardware.automotive.vehicle/current/android/hardware/automotive/vehicle/GetValueRequests.aidl b/automotive/vehicle/aidl/aidl_api/android.hardware.automotive.vehicle/current/android/hardware/automotive/vehicle/GetValueRequests.aidl
index c263a9d..5ecf4dd 100644
--- a/automotive/vehicle/aidl/aidl_api/android.hardware.automotive.vehicle/current/android/hardware/automotive/vehicle/GetValueRequests.aidl
+++ b/automotive/vehicle/aidl/aidl_api/android.hardware.automotive.vehicle/current/android/hardware/automotive/vehicle/GetValueRequests.aidl
@@ -34,7 +34,6 @@
package android.hardware.automotive.vehicle;
@VintfStability
parcelable GetValueRequests {
- android.hardware.automotive.vehicle.VehiclePropValue[] payloads;
- long[] requestId;
+ android.hardware.automotive.vehicle.GetValueRequest[] payloads;
@nullable ParcelFileDescriptor sharedMemoryFd;
}
diff --git a/automotive/vehicle/aidl/aidl_api/android.hardware.automotive.vehicle/current/android/hardware/automotive/vehicle/SetValueRequest.aidl b/automotive/vehicle/aidl/aidl_api/android.hardware.automotive.vehicle/current/android/hardware/automotive/vehicle/SetValueRequest.aidl
index 82e7551..d6cddb6 100644
--- a/automotive/vehicle/aidl/aidl_api/android.hardware.automotive.vehicle/current/android/hardware/automotive/vehicle/SetValueRequest.aidl
+++ b/automotive/vehicle/aidl/aidl_api/android.hardware.automotive.vehicle/current/android/hardware/automotive/vehicle/SetValueRequest.aidl
@@ -34,6 +34,6 @@
package android.hardware.automotive.vehicle;
@VintfStability
parcelable SetValueRequest {
- android.hardware.automotive.vehicle.VehiclePropValue value;
long requestId;
+ android.hardware.automotive.vehicle.VehiclePropValue value;
}
diff --git a/automotive/vehicle/aidl/aidl_api/android.hardware.automotive.vehicle/current/android/hardware/automotive/vehicle/VehicleProperty.aidl b/automotive/vehicle/aidl/aidl_api/android.hardware.automotive.vehicle/current/android/hardware/automotive/vehicle/VehicleProperty.aidl
index 4af3e5b..3fc9353 100644
--- a/automotive/vehicle/aidl/aidl_api/android.hardware.automotive.vehicle/current/android/hardware/automotive/vehicle/VehicleProperty.aidl
+++ b/automotive/vehicle/aidl/aidl_api/android.hardware.automotive.vehicle/current/android/hardware/automotive/vehicle/VehicleProperty.aidl
@@ -105,7 +105,7 @@
EV_BATTERY_DISPLAY_UNITS = 289408515,
FUEL_CONSUMPTION_UNITS_DISTANCE_OVER_VOLUME = 287311364,
VEHICLE_SPEED_DISPLAY_UNITS = 289408517,
- EPOCH_TIME = 290457094,
+ ANDROID_EPOCH_TIME = 290457094,
STORAGE_ENCRYPTION_BINDING_SEED = 292554247,
ENV_OUTSIDE_TEMPERATURE = 291505923,
AP_POWER_STATE_REQ = 289475072,
diff --git a/automotive/vehicle/aidl/android/hardware/automotive/vehicle/GetValueRequest.aidl b/automotive/vehicle/aidl/android/hardware/automotive/vehicle/GetValueRequest.aidl
new file mode 100644
index 0000000..43a89d0
--- /dev/null
+++ b/automotive/vehicle/aidl/android/hardware/automotive/vehicle/GetValueRequest.aidl
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2021 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.automotive.vehicle;
+
+import android.hardware.automotive.vehicle.VehiclePropValue;
+
+@VintfStability
+parcelable GetValueRequest {
+ // A unique request ID. For every client, the request ID must start with 1
+ // and monotonically increase for every SetValueRequest. If it hits
+ // LONG_MAX (very unlikely), it must loop back to 0.
+ long requestId;
+ // The property to get, for most properties, only the property ID or area
+ // ID would be used to identify the property, but for some properties, other
+ // fields might be used.
+ VehiclePropValue prop;
+}
diff --git a/automotive/vehicle/aidl/android/hardware/automotive/vehicle/GetValueRequests.aidl b/automotive/vehicle/aidl/android/hardware/automotive/vehicle/GetValueRequests.aidl
index 149799e..6601233 100644
--- a/automotive/vehicle/aidl/android/hardware/automotive/vehicle/GetValueRequests.aidl
+++ b/automotive/vehicle/aidl/android/hardware/automotive/vehicle/GetValueRequests.aidl
@@ -16,18 +16,14 @@
package android.hardware.automotive.vehicle;
-import android.hardware.automotive.vehicle.VehiclePropValue;
+import android.hardware.automotive.vehicle.GetValueRequest;
import android.os.ParcelFileDescriptor;
@VintfStability
parcelable GetValueRequests {
- // The list of properties to get if they fit the binder memory limitation.
- VehiclePropValue[] payloads;
- // A list of request IDs, one for each VehiclePropValue. For every client,
- // the request ID must start with 1 and monotonically increase for every
- // VehiclePropValue in GetValueRequests. If it hits LONG_MAX (very
- // unlikely), it must loop back to 0.
- long[] requestId;
+ // A list of GetValueRequest to get if they fit the binder memory
+ // limitation.
+ GetValueRequest[] payloads;
// Shared memory file to store requests if they exceed binder memory
// limitation. Created by client, readable only at VHAL during the call.
// Caller could close it after the call.
diff --git a/automotive/vehicle/aidl/android/hardware/automotive/vehicle/SetValueRequest.aidl b/automotive/vehicle/aidl/android/hardware/automotive/vehicle/SetValueRequest.aidl
index 1771c63..f2b0e27 100644
--- a/automotive/vehicle/aidl/android/hardware/automotive/vehicle/SetValueRequest.aidl
+++ b/automotive/vehicle/aidl/android/hardware/automotive/vehicle/SetValueRequest.aidl
@@ -20,10 +20,10 @@
@VintfStability
parcelable SetValueRequest {
- // The value to set.
- VehiclePropValue value;
// A unique request ID. For every client, the request ID must start with 1
// and monotonically increase for every SetValueRequest. If it hits
// LONG_MAX (very unlikely), it must loop back to 0.
long requestId;
+ // The value to set.
+ VehiclePropValue value;
}
diff --git a/automotive/vehicle/aidl/android/hardware/automotive/vehicle/VehicleProperty.aidl b/automotive/vehicle/aidl/android/hardware/automotive/vehicle/VehicleProperty.aidl
index 65a22db..e3b5a38 100644
--- a/automotive/vehicle/aidl/android/hardware/automotive/vehicle/VehicleProperty.aidl
+++ b/automotive/vehicle/aidl/android/hardware/automotive/vehicle/VehicleProperty.aidl
@@ -1035,24 +1035,32 @@
VEHICLE_SPEED_DISPLAY_UNITS = 0x0605 + 0x10000000 + 0x01000000
+ 0x00400000, // VehiclePropertyGroup:SYSTEM,VehicleArea:GLOBAL,VehiclePropertyType:INT32
/**
- * Current date and time, encoded as Unix time (in milliseconds).
+ * Current date and time, encoded as Epoch time (in milliseconds).
* This value denotes the number of milliseconds seconds that have
* elapsed since 1/1/1970 UTC.
*
- * Reading this value will give you the system’s time. This can be
- * useful to synchronize other vehicle systems (dash clock etc).
+ * CarServices will write to this value to give VHAL the Android system's
+ * time, if the VHAL supports this property. This can be useful to
+ * synchronize other vehicle systems (dash clock etc) with Android's time.
*
- * Writing this value will update the ‘ExternalTimeSuggestion’
- * value (if enabled). This value may be consumed by the “Time
- * Detector Service”, if other sources do not have a higher
- * priority. For information on how to adjust time source
- * priorities see Time Detector Service documentation.
+ * AAOS writes to this property once during boot, and
+ * will thereafter write only when some time-source changes are propagated.
+ * AAOS will fill in VehiclePropValue.timestamp correctly.
+ * Note that AAOS will not send updates for natural elapse of time.
+ * int64Values[0] = provided Unix time (in milliseconds)
+ *
+ * Note that the property may take >0 ms to get propagated through the stack
+ * and, having a timestamped property helps reduce any time drift. So,
+ * for all writes to the property, the timestamp can be used to negate this
+ * drift:
+ * drift = elapsedTime - PropValue.timestamp
+ * effectiveTime = PropValue.value.int64Values[0] + drift
*
* @change_mode VehiclePropertyChangeMode:ON_CHANGE
- * @access VehiclePropertyAccess:READ_WRITE
+ * @access VehiclePropertyAccess:WRITE_ONLY
* @unit VehicleUnit:MILLI_SECS
*/
- EPOCH_TIME = 0x0606 + 0x10000000 + 0x01000000
+ ANDROID_EPOCH_TIME = 0x0606 + 0x10000000 + 0x01000000
+ 0x00500000, // VehiclePropertyGroup:SYSTEM,VehicleArea:GLOBAL,VehiclePropertyType:INT64
/**
* External encryption binding seed.
diff --git a/automotive/vehicle/aidl/impl/default_config/include/DefaultConfig.h b/automotive/vehicle/aidl/impl/default_config/include/DefaultConfig.h
index 12a5691..d2b69af 100644
--- a/automotive/vehicle/aidl/impl/default_config/include/DefaultConfig.h
+++ b/automotive/vehicle/aidl/impl/default_config/include/DefaultConfig.h
@@ -1016,8 +1016,8 @@
{
.config =
{
- .prop = toInt(VehicleProperty::EPOCH_TIME),
- .access = VehiclePropertyAccess::READ_WRITE,
+ .prop = toInt(VehicleProperty::ANDROID_EPOCH_TIME),
+ .access = VehiclePropertyAccess::WRITE,
.changeMode = VehiclePropertyChangeMode::ON_CHANGE,
},
},
diff --git a/automotive/vehicle/aidl/impl/fake_impl/GeneratorHub/Android.bp b/automotive/vehicle/aidl/impl/fake_impl/GeneratorHub/Android.bp
new file mode 100644
index 0000000..4735313
--- /dev/null
+++ b/automotive/vehicle/aidl/impl/fake_impl/GeneratorHub/Android.bp
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2021 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 {
+ default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+cc_library {
+ name: "FakeVehicleHalValueGenerators",
+ vendor: true,
+ srcs: ["src/*.cpp"],
+ local_include_dirs: ["include"],
+ export_include_dirs: ["include"],
+ defaults: ["VehicleHalDefaults"],
+ static_libs: ["VehicleHalUtils"],
+ shared_libs: [
+ "libjsoncpp",
+ ],
+}
diff --git a/automotive/vehicle/aidl/impl/fake_impl/GeneratorHub/include/FakeValueGenerator.h b/automotive/vehicle/aidl/impl/fake_impl/GeneratorHub/include/FakeValueGenerator.h
new file mode 100644
index 0000000..93ffebf
--- /dev/null
+++ b/automotive/vehicle/aidl/impl/fake_impl/GeneratorHub/include/FakeValueGenerator.h
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2021 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_aidl_impl_fake_impl_GeneratorHub_include_FakeValueGenerator_H_
+#define android_hardware_automotive_vehicle_aidl_impl_fake_impl_GeneratorHub_include_FakeValueGenerator_H_
+
+#include <VehicleHalTypes.h>
+
+#include <optional>
+
+namespace android {
+namespace hardware {
+namespace automotive {
+namespace vehicle {
+namespace fake {
+
+// A abstract class for all fake value generators.
+class FakeValueGenerator {
+ public:
+ virtual ~FakeValueGenerator() = default;
+
+ // Returns the next event if there is one or {@code std::nullopt} if there is none.
+ virtual std::optional<::aidl::android::hardware::automotive::vehicle::VehiclePropValue>
+ nextEvent() = 0;
+};
+
+} // namespace fake
+} // namespace vehicle
+} // namespace automotive
+} // namespace hardware
+} // namespace android
+
+#endif // android_hardware_automotive_vehicle_aidl_impl_fake_impl_GeneratorHub_include_FakeValueGenerator_H_
diff --git a/automotive/vehicle/aidl/impl/fake_impl/GeneratorHub/include/GeneratorHub.h b/automotive/vehicle/aidl/impl/fake_impl/GeneratorHub/include/GeneratorHub.h
new file mode 100644
index 0000000..ad04d23
--- /dev/null
+++ b/automotive/vehicle/aidl/impl/fake_impl/GeneratorHub/include/GeneratorHub.h
@@ -0,0 +1,92 @@
+/*
+ * Copyright (C) 2021 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_interfaces_automotive_vehicle_aidl_fake_impl_GeneratorHub_include_GeneratorHub_h_
+#define android_hardware_interfaces_automotive_vehicle_aidl_fake_impl_GeneratorHub_include_GeneratorHub_h_
+
+#include "FakeValueGenerator.h"
+
+#include <android-base/thread_annotations.h>
+
+#include <atomic>
+#include <chrono>
+#include <condition_variable>
+#include <iostream>
+#include <mutex>
+#include <optional>
+#include <queue>
+#include <thread>
+#include <unordered_map>
+
+namespace android {
+namespace hardware {
+namespace automotive {
+namespace vehicle {
+namespace fake {
+
+// This is the scheduler for all VHAL event generators. It manages all generators and uses priority
+// queue to maintain generated events ordered by timestamp. The scheduler uses a single thread to
+// keep querying and updating the event queue to make sure events from all generators are produced
+// in order.
+class GeneratorHub {
+ public:
+ using OnHalEvent = std::function<void(
+ const ::aidl::android::hardware::automotive::vehicle::VehiclePropValue& event)>;
+
+ explicit GeneratorHub(OnHalEvent&& onHalEvent);
+ ~GeneratorHub();
+
+ // Register a new generator. The generator will be discarded if it could not produce next event.
+ // The existing generator will be overridden if it has the same generatorId.
+ void registerGenerator(int32_t generatorId, std::unique_ptr<FakeValueGenerator> generator);
+
+ // Unregister a generator with the generatorId. If no registered generator is found, this
+ // function does nothing.
+ void unregisterGenerator(int32_t generatorId);
+
+ private:
+ struct VhalEvent {
+ int32_t generatorId;
+ ::aidl::android::hardware::automotive::vehicle::VehiclePropValue val;
+ };
+
+ // Comparator used by priority queue to keep track of soonest event.
+ struct GreaterByTime {
+ bool operator()(const VhalEvent& lhs, const VhalEvent& rhs) const {
+ return lhs.val.timestamp > rhs.val.timestamp;
+ }
+ };
+
+ std::priority_queue<VhalEvent, std::vector<VhalEvent>, GreaterByTime> mEventQueue;
+ std::mutex mGeneratorsLock;
+ std::unordered_map<int32_t, std::unique_ptr<FakeValueGenerator>> mGenerators
+ GUARDED_BY(mGeneratorsLock);
+ OnHalEvent mOnHalEvent;
+ std::condition_variable mCond;
+ std::thread mThread;
+ std::atomic<bool> mShuttingDownFlag{false};
+
+ // Main loop of the single thread to producing event and updating event queue.
+ void run();
+};
+
+} // namespace fake
+} // namespace vehicle
+} // namespace automotive
+} // namespace hardware
+} // namespace android
+
+#endif // android_hardware_interfaces_automotive_vehicle_aidl_fake_impl_GeneratorHub_include_GeneratorHub_h_
diff --git a/automotive/vehicle/aidl/impl/fake_impl/GeneratorHub/include/JsonFakeValueGenerator.h b/automotive/vehicle/aidl/impl/fake_impl/GeneratorHub/include/JsonFakeValueGenerator.h
new file mode 100644
index 0000000..8116ed2
--- /dev/null
+++ b/automotive/vehicle/aidl/impl/fake_impl/GeneratorHub/include/JsonFakeValueGenerator.h
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2021 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_aidl_impl_fake_impl_GeneratorHub_include_JsonFakeValueGenerator_H_
+#define android_hardware_automotive_vehicle_aidl_impl_fake_impl_GeneratorHub_include_JsonFakeValueGenerator_H_
+
+#include "FakeValueGenerator.h"
+
+#include <json/json.h>
+
+#include <iostream>
+#include <vector>
+
+namespace android {
+namespace hardware {
+namespace automotive {
+namespace vehicle {
+namespace fake {
+
+class JsonFakeValueGenerator : public FakeValueGenerator {
+ public:
+ // Create a new JSON fake value generator. {@code request.value.stringValue} is the JSON file
+ // name. {@code request.value.int32Values[1]} if exists, is the number of iterations. If
+ // {@code int32Values} has less than 2 elements, number of iterations would be set to -1, which
+ // means iterate indefinitely.
+ explicit JsonFakeValueGenerator(
+ const ::aidl::android::hardware::automotive::vehicle::VehiclePropValue& request);
+ // Create a new JSON fake value generator using the specified JSON file path. All the events
+ // in the JSON file would be generated for number of {@code iteration}. If iteration is 0, no
+ // value would be generated. If iteration is less than 0, it would iterate indefinitely.
+ explicit JsonFakeValueGenerator(const std::string& path, int32_t iteration);
+ // Create a new JSON fake value generator using the specified JSON file path. All the events
+ // in the JSON file would be generated once.
+ explicit JsonFakeValueGenerator(const std::string& path);
+
+ ~JsonFakeValueGenerator() = default;
+
+ std::optional<::aidl::android::hardware::automotive::vehicle::VehiclePropValue> nextEvent()
+ override;
+ const std::vector<::aidl::android::hardware::automotive::vehicle::VehiclePropValue>&
+ getAllEvents();
+
+ private:
+ size_t mEventIndex = 0;
+ std::vector<::aidl::android::hardware::automotive::vehicle::VehiclePropValue> mEvents;
+ long mLastEventTimestamp = 0;
+ int32_t mNumOfIterations = 0;
+
+ void setBit(std::vector<uint8_t>& bytes, size_t idx);
+ void init(const std::string& path, int32_t iteration);
+};
+
+} // namespace fake
+} // namespace vehicle
+} // namespace automotive
+} // namespace hardware
+} // namespace android
+
+#endif // android_hardware_automotive_vehicle_aidl_impl_fake_impl_GeneratorHub_include_JsonFakeValueGenerator_H_
diff --git a/automotive/vehicle/aidl/impl/fake_impl/GeneratorHub/include/LinearFakeValueGenerator.h b/automotive/vehicle/aidl/impl/fake_impl/GeneratorHub/include/LinearFakeValueGenerator.h
new file mode 100644
index 0000000..bd004f3
--- /dev/null
+++ b/automotive/vehicle/aidl/impl/fake_impl/GeneratorHub/include/LinearFakeValueGenerator.h
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) 2021 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_aidl_impl_fake_impl_GeneratorHub_include_LinearFakeValueGenerator_H_
+#define android_hardware_automotive_vehicle_aidl_impl_fake_impl_GeneratorHub_include_LinearFakeValueGenerator_H_
+
+#include "FakeValueGenerator.h"
+
+namespace android {
+namespace hardware {
+namespace automotive {
+namespace vehicle {
+namespace fake {
+
+class LinearFakeValueGenerator : public FakeValueGenerator {
+ public:
+ // A linear value generator initialized using values in request.
+ // int32Values[1]: propId
+ // floatValues[0]: middleValue and currentValue
+ // floatValues[1]: dispersion
+ // floatValues[2]: increment
+ // int64Values[0]: interval
+ // {@code propId} must be INT32 or INT64 or FLOAT type.
+ explicit LinearFakeValueGenerator(
+ const ::aidl::android::hardware::automotive::vehicle::VehiclePropValue& request);
+ // A linear value generator in range [middleValue - dispersion, middleValue + dispersion),
+ // starts at 'currentValue' and at each 'interval', increase by 'increment' and loop back if
+ // exceeds middleValue + dispersion. {@code propId} must be INT32 or INT64 or FLOAT type.
+ explicit LinearFakeValueGenerator(int32_t propId, float middleValue, float initValue,
+ float dispersion, float increment, int64_t interval);
+ ~LinearFakeValueGenerator() = default;
+
+ std::optional<::aidl::android::hardware::automotive::vehicle::VehiclePropValue> nextEvent()
+ override;
+
+ private:
+ // In every timer tick we may want to generate new value based on initial value for debug
+ // purpose. It's better to have sequential values to see if events gets delivered in order
+ // to the client.
+ struct GeneratorCfg {
+ int32_t propId;
+ float middleValue;
+ float currentValue; // Should be in range (middleValue +/- dispersion).
+ float dispersion; // Defines minimum and maximum value based on initial value.
+ float increment; // Value that we will be added to currentValue with each timer tick.
+ int64_t interval;
+ long lastEventTimestamp;
+ };
+
+ GeneratorCfg mGenCfg;
+
+ void initGenCfg(int32_t propId, float middleValue, float initValue, float dispersion,
+ float increment, int64_t interval);
+};
+
+} // namespace fake
+} // namespace vehicle
+} // namespace automotive
+} // namespace hardware
+} // namespace android
+
+#endif // android_hardware_automotive_vehicle_aidl_impl_fake_impl_GeneratorHub_include_LinearFakeValueGenerator_H_
diff --git a/automotive/vehicle/aidl/impl/fake_impl/GeneratorHub/src/GeneratorHub.cpp b/automotive/vehicle/aidl/impl/fake_impl/GeneratorHub/src/GeneratorHub.cpp
new file mode 100644
index 0000000..0c182d9
--- /dev/null
+++ b/automotive/vehicle/aidl/impl/fake_impl/GeneratorHub/src/GeneratorHub.cpp
@@ -0,0 +1,123 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "GeneratorHub"
+
+#include "GeneratorHub.h"
+
+#include <utils/Log.h>
+#include <utils/SystemClock.h>
+
+namespace android {
+namespace hardware {
+namespace automotive {
+namespace vehicle {
+namespace fake {
+
+using ::android::base::ScopedLockAssertion;
+
+GeneratorHub::GeneratorHub(OnHalEvent&& onHalEvent)
+ : mOnHalEvent(onHalEvent), mThread(&GeneratorHub::run, this) {}
+
+GeneratorHub::~GeneratorHub() {
+ mShuttingDownFlag.store(true);
+ mCond.notify_all();
+ if (mThread.joinable()) {
+ mThread.join();
+ }
+}
+
+void GeneratorHub::registerGenerator(int32_t id, std::unique_ptr<FakeValueGenerator> generator) {
+ {
+ std::scoped_lock<std::mutex> lockGuard(mGeneratorsLock);
+ auto maybeNextEvent = generator->nextEvent();
+ // Register only if the generator can produce at least one event.
+ if (maybeNextEvent.has_value()) {
+ // Push the next event if it is a new generator
+ if (mGenerators.find(id) == mGenerators.end()) {
+ ALOGI("%s: Registering new generator, id: %d", __func__, id);
+ mEventQueue.push({id, maybeNextEvent.value()});
+ }
+ mGenerators[id] = std::move(generator);
+ ALOGI("%s: Registered generator, id: %d", __func__, id);
+ }
+ }
+ mCond.notify_one();
+}
+
+void GeneratorHub::unregisterGenerator(int32_t id) {
+ {
+ std::scoped_lock<std::mutex> lockGuard(mGeneratorsLock);
+ mGenerators.erase(id);
+ }
+ mCond.notify_one();
+ ALOGI("%s: Unregistered generator, id: %d", __func__, id);
+}
+
+void GeneratorHub::run() {
+ while (!mShuttingDownFlag.load()) {
+ std::unique_lock<std::mutex> lock(mGeneratorsLock);
+ ScopedLockAssertion lock_assertion(mGeneratorsLock);
+ // Pop events whose generator does not exist (may be already unregistered)
+ while (!mEventQueue.empty() &&
+ mGenerators.find(mEventQueue.top().generatorId) == mGenerators.end()) {
+ mEventQueue.pop();
+ }
+ // Wait until event queue is not empty or shutting down flag is set.
+ // This would unlock mGeneratorsLock and reacquire later.
+ mCond.wait(lock, [this] { return !mEventQueue.empty() || mShuttingDownFlag.load(); });
+ if (mShuttingDownFlag.load()) {
+ break;
+ }
+
+ const VhalEvent& curEvent = mEventQueue.top();
+ long currentTime = elapsedRealtimeNano();
+ long waitTime =
+ curEvent.val.timestamp > currentTime ? curEvent.val.timestamp - currentTime : 0;
+ if (waitTime != 0) {
+ // Wait until the soonest event happen
+ if (mCond.wait_for(lock, std::chrono::nanoseconds(waitTime)) !=
+ std::cv_status::timeout) {
+ // It is possible that a new generator is registered and produced a sooner event, or
+ // current generator is unregistered, in this case the thread will re-evaluate the
+ // soonest event
+ ALOGI("Something happened while waiting");
+ continue;
+ }
+ }
+ // Now it's time to handle current event.
+ mOnHalEvent(curEvent.val);
+ // Update queue by popping current event and producing next event from the same generator
+ int32_t id = curEvent.generatorId;
+ mEventQueue.pop();
+ if (mGenerators.find(id) != mGenerators.end()) {
+ auto maybeNextEvent = mGenerators[id]->nextEvent();
+ if (maybeNextEvent.has_value()) {
+ mEventQueue.push({id, maybeNextEvent.value()});
+ continue;
+ }
+ }
+
+ ALOGI("%s: Generator ended, unregister it, id: %d", __func__, id);
+ mGenerators.erase(id);
+ }
+}
+
+} // namespace fake
+} // namespace vehicle
+} // namespace automotive
+} // namespace hardware
+} // namespace android
diff --git a/automotive/vehicle/aidl/impl/fake_impl/GeneratorHub/src/JsonFakeValueGenerator.cpp b/automotive/vehicle/aidl/impl/fake_impl/GeneratorHub/src/JsonFakeValueGenerator.cpp
new file mode 100644
index 0000000..521ce45
--- /dev/null
+++ b/automotive/vehicle/aidl/impl/fake_impl/GeneratorHub/src/JsonFakeValueGenerator.cpp
@@ -0,0 +1,256 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "JsonFakeValueGenerator"
+
+#include "JsonFakeValueGenerator.h"
+
+#include <fstream>
+#include <type_traits>
+#include <typeinfo>
+
+#include <VehicleUtils.h>
+#include <android/binder_enums.h>
+#include <utils/Log.h>
+#include <utils/SystemClock.h>
+
+namespace android {
+namespace hardware {
+namespace automotive {
+namespace vehicle {
+namespace fake {
+
+namespace {
+
+using ::aidl::android::hardware::automotive::vehicle::DiagnosticFloatSensorIndex;
+using ::aidl::android::hardware::automotive::vehicle::DiagnosticIntegerSensorIndex;
+using ::aidl::android::hardware::automotive::vehicle::RawPropValues;
+using ::aidl::android::hardware::automotive::vehicle::VehicleProperty;
+using ::aidl::android::hardware::automotive::vehicle::VehiclePropertyType;
+using ::aidl::android::hardware::automotive::vehicle::VehiclePropValue;
+
+template <class T>
+int getLastIndex() {
+ auto range = ::ndk::enum_range<T>();
+ auto it = range.begin();
+ while (std::next(it) != range.end()) {
+ it++;
+ }
+ return toInt(*it);
+}
+
+bool isDiagnosticProperty(int32_t prop) {
+ return prop == toInt(VehicleProperty::OBD2_LIVE_FRAME) ||
+ prop == toInt(VehicleProperty::OBD2_FREEZE_FRAME);
+}
+
+void setBit(std::vector<uint8_t>& bytes, size_t idx) {
+ uint8_t mask = 1 << (idx % 8);
+ bytes[idx / 8] |= mask;
+}
+
+template <typename T>
+void copyJsonArray(const Json::Value& jsonArray, std::vector<T>& dest) {
+ dest.resize(jsonArray.size());
+ for (Json::Value::ArrayIndex i = 0; i < jsonArray.size(); i++) {
+ if (std::is_same<T, int32_t>::value) {
+ dest[i] = jsonArray[i].asInt();
+ } else if (std::is_same<T, int64_t>::value) {
+ dest[i] = jsonArray[i].asInt64();
+ } else if (std::is_same<T, float>::value) {
+ dest[i] = jsonArray[i].asFloat();
+ }
+ }
+}
+
+void copyMixedValueJson(const Json::Value& jsonValue, RawPropValues& dest) {
+ copyJsonArray(jsonValue["int32Values"], dest.int32Values);
+ copyJsonArray(jsonValue["int64Values"], dest.int64Values);
+ copyJsonArray(jsonValue["floatValues"], dest.floatValues);
+ dest.stringValue = jsonValue["stringValue"].asString();
+}
+
+std::vector<uint8_t> generateDiagnosticBytes(const RawPropValues& diagnosticValue) {
+ size_t lastIntegerSensorIndex =
+ static_cast<size_t>(getLastIndex<DiagnosticIntegerSensorIndex>());
+ size_t lastFloatSensorIndex = static_cast<size_t>(getLastIndex<DiagnosticFloatSensorIndex>());
+
+ size_t byteSize = (lastIntegerSensorIndex + lastFloatSensorIndex + 2);
+ std::vector<uint8_t> bytes((byteSize + 7) / 8);
+
+ auto& int32Values = diagnosticValue.int32Values;
+ for (size_t i = 0; i < int32Values.size(); i++) {
+ if (int32Values[i] != 0) {
+ setBit(bytes, i);
+ }
+ }
+
+ auto& floatValues = diagnosticValue.floatValues;
+ for (size_t i = 0; i < floatValues.size(); i++) {
+ if (floatValues[i] != 0.0) {
+ setBit(bytes, i + lastIntegerSensorIndex + 1);
+ }
+ }
+ return bytes;
+}
+
+std::vector<VehiclePropValue> parseFakeValueJson(std::istream& is) {
+ std::vector<VehiclePropValue> fakeVhalEvents;
+
+ Json::CharReaderBuilder builder;
+ Json::Value rawEvents;
+ std::string errorMessage;
+ if (!Json::parseFromStream(builder, is, &rawEvents, &errorMessage)) {
+ ALOGE("%s: Failed to parse fake data JSON file. Error: %s", __func__, errorMessage.c_str());
+ return fakeVhalEvents;
+ }
+
+ for (Json::Value::ArrayIndex i = 0; i < rawEvents.size(); i++) {
+ Json::Value rawEvent = rawEvents[i];
+ if (!rawEvent.isObject()) {
+ ALOGE("%s: VHAL JSON event should be an object, %s", __func__,
+ rawEvent.toStyledString().c_str());
+ continue;
+ }
+ if (rawEvent["prop"].empty() || rawEvent["areaId"].empty() || rawEvent["value"].empty() ||
+ rawEvent["timestamp"].empty()) {
+ ALOGE("%s: VHAL JSON event has missing fields, skip it, %s", __func__,
+ rawEvent.toStyledString().c_str());
+ continue;
+ }
+ VehiclePropValue event = {
+ .timestamp = rawEvent["timestamp"].asInt64(),
+ .areaId = rawEvent["areaId"].asInt(),
+ .prop = rawEvent["prop"].asInt(),
+ };
+
+ Json::Value rawEventValue = rawEvent["value"];
+ auto& value = event.value;
+ int32_t count;
+ switch (getPropType(event.prop)) {
+ case VehiclePropertyType::BOOLEAN:
+ case VehiclePropertyType::INT32:
+ value.int32Values.resize(1);
+ value.int32Values[0] = rawEventValue.asInt();
+ break;
+ case VehiclePropertyType::INT64:
+ value.int64Values.resize(1);
+ value.int64Values[0] = rawEventValue.asInt64();
+ break;
+ case VehiclePropertyType::FLOAT:
+ value.floatValues.resize(1);
+ value.floatValues[0] = rawEventValue.asFloat();
+ break;
+ case VehiclePropertyType::STRING:
+ value.stringValue = rawEventValue.asString();
+ break;
+ case VehiclePropertyType::INT32_VEC:
+ value.int32Values.resize(rawEventValue.size());
+ count = 0;
+ for (auto& it : rawEventValue) {
+ value.int32Values[count++] = it.asInt();
+ }
+ break;
+ case VehiclePropertyType::MIXED:
+ copyMixedValueJson(rawEventValue, value);
+ if (isDiagnosticProperty(event.prop)) {
+ value.byteValues = generateDiagnosticBytes(value);
+ }
+ break;
+ default:
+ ALOGE("%s: unsupported type for property: 0x%x", __func__, event.prop);
+ continue;
+ }
+ fakeVhalEvents.push_back(event);
+ }
+ return fakeVhalEvents;
+}
+
+} // namespace
+
+JsonFakeValueGenerator::JsonFakeValueGenerator(const std::string& path) {
+ init(path, 1);
+}
+
+JsonFakeValueGenerator::JsonFakeValueGenerator(const std::string& path, int32_t iteration) {
+ init(path, iteration);
+}
+
+JsonFakeValueGenerator::JsonFakeValueGenerator(const VehiclePropValue& request) {
+ const auto& v = request.value;
+ // Iterate infinitely if iteration number is not provided
+ int32_t numOfIterations = v.int32Values.size() < 2 ? -1 : v.int32Values[1];
+
+ init(v.stringValue, numOfIterations);
+}
+
+void JsonFakeValueGenerator::init(const std::string& path, int32_t iteration) {
+ std::ifstream ifs(path);
+ if (!ifs) {
+ ALOGE("%s: couldn't open %s for parsing.", __func__, path.c_str());
+ return;
+ }
+ mEvents = parseFakeValueJson(ifs);
+ mNumOfIterations = iteration;
+}
+
+const std::vector<VehiclePropValue>& JsonFakeValueGenerator::getAllEvents() {
+ return mEvents;
+}
+
+std::optional<VehiclePropValue> JsonFakeValueGenerator::nextEvent() {
+ if (mNumOfIterations == 0 || mEvents.size() == 0) {
+ return std::nullopt;
+ }
+
+ VehiclePropValue generatedValue = mEvents[mEventIndex];
+
+ if (mLastEventTimestamp == 0) {
+ mLastEventTimestamp = elapsedRealtimeNano();
+ } else {
+ long nextEventTime = 0;
+ if (mEventIndex > 0) {
+ // All events (start from 2nd one) are supposed to happen in the future with a delay
+ // equals to the duration between previous and current event.
+ nextEventTime = mLastEventTimestamp +
+ (mEvents[mEventIndex].timestamp - mEvents[mEventIndex - 1].timestamp);
+ } else {
+ // We are starting another iteration, immediately send the next event after 1ms.
+ nextEventTime = mLastEventTimestamp + 1000000;
+ }
+ // Prevent overflow.
+ assert(nextEventTime > mLastEventTimestamp);
+ mLastEventTimestamp = nextEventTime;
+ }
+
+ mEventIndex++;
+ if (mEventIndex == mEvents.size()) {
+ mEventIndex = 0;
+ if (mNumOfIterations > 0) {
+ mNumOfIterations--;
+ }
+ }
+
+ generatedValue.timestamp = mLastEventTimestamp;
+
+ return generatedValue;
+}
+
+} // namespace fake
+} // namespace vehicle
+} // namespace automotive
+} // namespace hardware
+} // namespace android
diff --git a/automotive/vehicle/aidl/impl/fake_impl/GeneratorHub/src/LinearFakeValueGenerator.cpp b/automotive/vehicle/aidl/impl/fake_impl/GeneratorHub/src/LinearFakeValueGenerator.cpp
new file mode 100644
index 0000000..9133144
--- /dev/null
+++ b/automotive/vehicle/aidl/impl/fake_impl/GeneratorHub/src/LinearFakeValueGenerator.cpp
@@ -0,0 +1,108 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "LinearFakeValueGenerator"
+
+#include "LinearFakeValueGenerator.h"
+
+#include <VehicleUtils.h>
+#include <utils/Log.h>
+#include <utils/SystemClock.h>
+
+namespace android {
+namespace hardware {
+namespace automotive {
+namespace vehicle {
+namespace fake {
+
+using ::aidl::android::hardware::automotive::vehicle::VehiclePropertyType;
+using ::aidl::android::hardware::automotive::vehicle::VehiclePropValue;
+
+LinearFakeValueGenerator::LinearFakeValueGenerator(int32_t propId, float middleValue,
+ float initValue, float dispersion,
+ float increment, int64_t interval) {
+ initGenCfg(propId, middleValue, initValue, dispersion, increment, interval);
+}
+
+LinearFakeValueGenerator::LinearFakeValueGenerator(const VehiclePropValue& request) {
+ const auto& v = request.value;
+ initGenCfg(v.int32Values[1], v.floatValues[0], v.floatValues[0], v.floatValues[1],
+ v.floatValues[2], v.int64Values[0]);
+}
+
+void LinearFakeValueGenerator::initGenCfg(int32_t propId, float middleValue, float initValue,
+ float dispersion, float increment, int64_t interval) {
+ // Other types are not supported.
+ assert(getPropType(propId) == VehicleProperty::INT32 ||
+ getPropType(propId) == VehicleProperty::INT64 ||
+ getPropType(propId) == VehicleProperty::FLOAT);
+
+ if (initValue < middleValue - dispersion || initValue >= middleValue + dispersion) {
+ ALOGW("%s: invalid initValue: %f, out of range, default to %f", __func__, initValue,
+ middleValue);
+ initValue = middleValue;
+ }
+ mGenCfg = GeneratorCfg{
+ .propId = propId,
+ .middleValue = middleValue,
+ .currentValue = initValue,
+ .dispersion = dispersion,
+ .increment = increment,
+ .interval = interval,
+ };
+}
+
+std::optional<VehiclePropValue> LinearFakeValueGenerator::nextEvent() {
+ VehiclePropValue event = {
+ .prop = mGenCfg.propId,
+ };
+ auto& value = event.value;
+ switch (getPropType(event.prop)) {
+ case VehiclePropertyType::INT32:
+ value.int32Values = {static_cast<int32_t>(mGenCfg.currentValue)};
+ break;
+ case VehiclePropertyType::INT64:
+ value.int64Values = {static_cast<int64_t>(mGenCfg.currentValue)};
+ break;
+ case VehiclePropertyType::FLOAT:
+ value.floatValues = {mGenCfg.currentValue};
+ break;
+ default:
+ ALOGE("%s: unsupported property type for 0x%x", __func__, event.prop);
+ }
+ if (mGenCfg.lastEventTimestamp == 0) {
+ mGenCfg.lastEventTimestamp = elapsedRealtimeNano();
+ } else {
+ long nextEventTime = mGenCfg.lastEventTimestamp + mGenCfg.interval;
+ // Prevent overflow.
+ assert(nextEventTime > mGenCfg.lastEventTimestamp);
+ mGenCfg.lastEventTimestamp = nextEventTime;
+ }
+ event.timestamp = mGenCfg.lastEventTimestamp;
+
+ mGenCfg.currentValue += mGenCfg.increment;
+ if (mGenCfg.currentValue >= mGenCfg.middleValue + mGenCfg.dispersion) {
+ // Wrap around, (i - d) + c - (i + d) = c - 2 * d
+ mGenCfg.currentValue = mGenCfg.currentValue - 2 * mGenCfg.dispersion;
+ }
+ return event;
+}
+
+} // namespace fake
+} // namespace vehicle
+} // namespace automotive
+} // namespace hardware
+} // namespace android
diff --git a/automotive/vehicle/aidl/impl/fake_impl/GeneratorHub/test/Android.bp b/automotive/vehicle/aidl/impl/fake_impl/GeneratorHub/test/Android.bp
new file mode 100644
index 0000000..d3d3a10
--- /dev/null
+++ b/automotive/vehicle/aidl/impl/fake_impl/GeneratorHub/test/Android.bp
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2021 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 {
+ default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+cc_test {
+ name: "FakeVehicleHalValueGeneratorsTest",
+ vendor: true,
+ srcs: ["*.cpp"],
+ defaults: ["VehicleHalDefaults"],
+ static_libs: [
+ "VehicleHalUtils",
+ "FakeVehicleHalValueGenerators",
+ ],
+ shared_libs: [
+ "libjsoncpp",
+ ],
+ data: [
+ ":FakeVehicleHalValueGeneratorsTestFiles",
+ ],
+ test_suites: ["device-tests"],
+}
+
+filegroup {
+ name: "FakeVehicleHalValueGeneratorsTestFiles",
+ srcs: [
+ "prop.json",
+ "prop_invalid.json",
+ ],
+}
diff --git a/automotive/vehicle/aidl/impl/fake_impl/GeneratorHub/test/FakeVehicleHalValueGeneratorsTest.cpp b/automotive/vehicle/aidl/impl/fake_impl/GeneratorHub/test/FakeVehicleHalValueGeneratorsTest.cpp
new file mode 100644
index 0000000..929d9b3
--- /dev/null
+++ b/automotive/vehicle/aidl/impl/fake_impl/GeneratorHub/test/FakeVehicleHalValueGeneratorsTest.cpp
@@ -0,0 +1,417 @@
+/*
+ * Copyright (C) 2021 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 <GeneratorHub.h>
+#include <JsonFakeValueGenerator.h>
+#include <LinearFakeValueGenerator.h>
+#include <VehicleUtils.h>
+#include <android-base/file.h>
+#include <android-base/thread_annotations.h>
+#include <gtest/gtest.h>
+#include <utils/SystemClock.h>
+
+#include <chrono>
+#include <memory>
+#include <mutex>
+#include <optional>
+#include <thread>
+#include <vector>
+
+namespace android {
+namespace hardware {
+namespace automotive {
+namespace vehicle {
+namespace fake {
+
+using ::aidl::android::hardware::automotive::vehicle::VehicleProperty;
+using ::aidl::android::hardware::automotive::vehicle::VehiclePropValue;
+
+class FakeVehicleHalValueGeneratorsTest : public ::testing::Test {
+ protected:
+ void SetUp() override {
+ mHub = std::make_unique<GeneratorHub>(
+ [this](const VehiclePropValue& event) { return onHalEvent(event); });
+ }
+
+ GeneratorHub* getHub() { return mHub.get(); }
+
+ std::vector<VehiclePropValue> getEvents() {
+ std::scoped_lock<std::mutex> lockGuard(mEventsLock);
+ return mEvents;
+ }
+
+ void clearEvents() {
+ std::scoped_lock<std::mutex> lockGuard(mEventsLock);
+ mEvents.clear();
+ }
+
+ void TearDown() override {
+ // Generator callback uses mEvents, must stop generator before destroying mEvents.
+ mHub.reset();
+ }
+
+ static std::string getTestFilePath(const char* filename) {
+ static std::string baseDir = android::base::GetExecutableDirectory();
+ return baseDir + "/" + filename;
+ }
+
+ private:
+ void onHalEvent(const VehiclePropValue& event) {
+ VehiclePropValue eventCopy = event;
+ std::scoped_lock<std::mutex> lockGuard(mEventsLock);
+ mEvents.push_back(std::move(eventCopy));
+ }
+
+ std::unique_ptr<GeneratorHub> mHub;
+ std::mutex mEventsLock;
+ std::vector<VehiclePropValue> mEvents GUARDED_BY(mEventsLock);
+};
+
+class TestFakeValueGenerator : public FakeValueGenerator {
+ public:
+ void setEvents(const std::vector<VehiclePropValue>& events) {
+ mEvents = events;
+ mEventIndex = 0;
+ }
+
+ std::optional<::aidl::android::hardware::automotive::vehicle::VehiclePropValue> nextEvent()
+ override {
+ if (mEventIndex == mEvents.size()) {
+ return std::nullopt;
+ }
+ return mEvents[mEventIndex++];
+ }
+
+ private:
+ std::vector<VehiclePropValue> mEvents;
+ size_t mEventIndex = 0;
+};
+
+TEST_F(FakeVehicleHalValueGeneratorsTest, testRegisterTestFakeValueGenerator) {
+ auto generator = std::make_unique<TestFakeValueGenerator>();
+ std::vector<VehiclePropValue> events;
+ size_t eventCount = 10;
+ int64_t timestamp = elapsedRealtimeNano();
+ for (size_t i = 0; i < eventCount; i++) {
+ events.push_back(VehiclePropValue{
+ .prop = static_cast<int32_t>(i),
+ .timestamp = timestamp + static_cast<int64_t>(50 * i),
+ });
+ }
+ generator->setEvents(events);
+
+ getHub()->registerGenerator(0, std::move(generator));
+
+ // All the events require 500ms to generate, so waiting for 1000ms should be enough.
+ std::this_thread::sleep_for(std::chrono::milliseconds(1000));
+
+ ASSERT_EQ(getEvents(), events);
+
+ getHub()->unregisterGenerator(0);
+}
+
+TEST_F(FakeVehicleHalValueGeneratorsTest, testUnregisterGeneratorStopGeneration) {
+ auto generator = std::make_unique<TestFakeValueGenerator>();
+ std::vector<VehiclePropValue> events;
+ size_t eventCount = 10;
+ int64_t timestamp = elapsedRealtimeNano();
+ for (size_t i = 0; i < eventCount; i++) {
+ events.push_back(VehiclePropValue{
+ .prop = static_cast<int32_t>(i),
+ .timestamp = timestamp + static_cast<int64_t>(50 * i),
+ });
+ }
+ generator->setEvents(events);
+
+ getHub()->registerGenerator(0, std::move(generator));
+ getHub()->unregisterGenerator(0);
+
+ std::this_thread::sleep_for(std::chrono::milliseconds(1000));
+
+ ASSERT_LT(getEvents().size(), static_cast<size_t>(10))
+ << "Must stop generating event after generator is unregistered";
+}
+
+TEST_F(FakeVehicleHalValueGeneratorsTest, testLinerFakeValueGeneratorFloat) {
+ std::unique_ptr<LinearFakeValueGenerator> generator =
+ std::make_unique<LinearFakeValueGenerator>(toInt(VehicleProperty::PERF_VEHICLE_SPEED),
+ /*middleValue=*/50.0,
+ /*initValue=*/30.0,
+ /*dispersion=*/50.0,
+ /*increment=*/20.0,
+ /*interval=*/10000000);
+ getHub()->registerGenerator(0, std::move(generator));
+
+ std::this_thread::sleep_for(std::chrono::milliseconds(100));
+
+ auto events = getEvents();
+ // We should get 10 events ideally, but let's be safe here.
+ ASSERT_LE((size_t)5, events.size());
+ int value = 30;
+ for (size_t i = 0; i < 5; i++) {
+ EXPECT_EQ(std::vector<float>({static_cast<float>(value)}), events[i].value.floatValues);
+ value = (value + 20) % 100;
+ }
+}
+
+TEST_F(FakeVehicleHalValueGeneratorsTest, testLinerFakeValueGeneratorInt32) {
+ std::unique_ptr<LinearFakeValueGenerator> generator =
+ std::make_unique<LinearFakeValueGenerator>(toInt(VehicleProperty::INFO_MODEL_YEAR),
+ /*middleValue=*/50.0,
+ /*initValue=*/30.0,
+ /*dispersion=*/50.0,
+ /*increment=*/20.0,
+ /*interval=*/10000000);
+ getHub()->registerGenerator(0, std::move(generator));
+
+ std::this_thread::sleep_for(std::chrono::milliseconds(100));
+
+ auto events = getEvents();
+ // We should get 10 events ideally, but let's be safe here.
+ ASSERT_LE((size_t)5, events.size());
+ int value = 30;
+ for (size_t i = 0; i < 5; i++) {
+ EXPECT_EQ(std::vector<int32_t>({value}), events[i].value.int32Values);
+ value = (value + 20) % 100;
+ }
+}
+
+TEST_F(FakeVehicleHalValueGeneratorsTest, testLinerFakeValueGeneratorInt64) {
+ std::unique_ptr<LinearFakeValueGenerator> generator =
+ std::make_unique<LinearFakeValueGenerator>(toInt(VehicleProperty::EPOCH_TIME),
+ /*middleValue=*/50.0,
+ /*initValue=*/30.0,
+ /*dispersion=*/50.0,
+ /*increment=*/20.0,
+ /*interval=*/10000000);
+ getHub()->registerGenerator(0, std::move(generator));
+
+ std::this_thread::sleep_for(std::chrono::milliseconds(100));
+
+ auto events = getEvents();
+ // We should get 10 events ideally, but let's be safe here.
+ ASSERT_LE((size_t)5, events.size());
+ int value = 30;
+ for (size_t i = 0; i < 5; i++) {
+ EXPECT_EQ(std::vector<int64_t>({value}), events[i].value.int64Values);
+ value = (value + 20) % 100;
+ }
+}
+
+TEST_F(FakeVehicleHalValueGeneratorsTest, testLinerFakeValueGeneratorUsingRequest) {
+ VehiclePropValue request;
+ request.value.int32Values = {0, toInt(VehicleProperty::PERF_VEHICLE_SPEED)};
+ request.value.floatValues = {/*middleValue=*/50.0, /*dispersion=*/50.0, /*increment=*/20.0};
+ request.value.int64Values = {/*interval=*/10000000};
+
+ std::unique_ptr<LinearFakeValueGenerator> generator =
+ std::make_unique<LinearFakeValueGenerator>(request);
+ getHub()->registerGenerator(0, std::move(generator));
+
+ std::this_thread::sleep_for(std::chrono::milliseconds(100));
+
+ auto events = getEvents();
+ // We should get 10 events ideally, but let's be safe here.
+ ASSERT_LE((size_t)5, events.size());
+ int value = 50;
+ for (size_t i = 0; i < 5; i++) {
+ EXPECT_EQ(std::vector<float>({static_cast<float>(value)}), events[i].value.floatValues);
+ value = (value + 20) % 100;
+ }
+}
+
+TEST_F(FakeVehicleHalValueGeneratorsTest, testLinerFakeValueGeneratorInvalidInitValue) {
+ std::unique_ptr<LinearFakeValueGenerator> generator =
+ std::make_unique<LinearFakeValueGenerator>(toInt(VehicleProperty::PERF_VEHICLE_SPEED),
+ /*middleValue=*/50.0,
+ // Out of range
+ /*initValue=*/110.0,
+ /*dispersion=*/50.0,
+ /*increment=*/20.0,
+ /*interval=*/10000000);
+ getHub()->registerGenerator(0, std::move(generator));
+
+ std::this_thread::sleep_for(std::chrono::milliseconds(100));
+
+ auto events = getEvents();
+ // We should get 10 events ideally, but let's be safe here.
+ ASSERT_LE((size_t)5, events.size());
+
+ // Init value would be set to middleValue if given initValue is not valid.
+ int value = 50;
+ for (size_t i = 0; i < 5; i++) {
+ EXPECT_EQ(std::vector<float>({static_cast<float>(value)}), events[i].value.floatValues);
+ value = (value + 20) % 100;
+ }
+}
+
+TEST_F(FakeVehicleHalValueGeneratorsTest, testJsonFakeValueGenerator) {
+ long currentTime = elapsedRealtimeNano();
+
+ std::unique_ptr<JsonFakeValueGenerator> generator =
+ std::make_unique<JsonFakeValueGenerator>(getTestFilePath("prop.json"), 2);
+ getHub()->registerGenerator(0, std::move(generator));
+
+ // wait for some time.
+ std::this_thread::sleep_for(std::chrono::milliseconds(100));
+
+ std::vector<VehiclePropValue> expectedValues = {
+ VehiclePropValue{
+ .areaId = 0,
+ .value.int32Values = {8},
+ .prop = 289408000,
+ },
+ VehiclePropValue{
+ .areaId = 0,
+ .value.int32Values = {4},
+ .prop = 289408000,
+ },
+ VehiclePropValue{
+ .areaId = 0,
+ .value.int32Values = {16},
+ .prop = 289408000,
+ },
+ VehiclePropValue{
+ .areaId = 0,
+ .value.int32Values = {10},
+ .prop = 289408000,
+ },
+ };
+
+ // We have two iterations.
+ for (size_t i = 0; i < 4; i++) {
+ expectedValues.push_back(expectedValues[i]);
+ }
+
+ auto events = getEvents();
+
+ long lastEventTime = currentTime;
+ for (auto& event : events) {
+ EXPECT_GT(event.timestamp, lastEventTime);
+ lastEventTime = event.timestamp;
+ event.timestamp = 0;
+ }
+
+ EXPECT_EQ(events, expectedValues);
+}
+
+TEST_F(FakeVehicleHalValueGeneratorsTest, testJsonFakeValueGeneratorIterateIndefinitely) {
+ std::unique_ptr<JsonFakeValueGenerator> generator =
+ std::make_unique<JsonFakeValueGenerator>(getTestFilePath("prop.json"), -1);
+ getHub()->registerGenerator(0, std::move(generator));
+
+ // wait for some time.
+ std::this_thread::sleep_for(std::chrono::milliseconds(100));
+
+ auto events = getEvents();
+
+ // Send 1 iteration takes 4ms + 1ms interval between iteration, so for 100ms we should get about
+ // 20 iteration, which is 80 events.
+ EXPECT_GT(events.size(), static_cast<size_t>(50));
+}
+
+TEST_F(FakeVehicleHalValueGeneratorsTest, testJsonFakeValueGeneratorUsingRequest) {
+ long currentTime = elapsedRealtimeNano();
+
+ VehiclePropValue request = {.value = {
+ .stringValue = getTestFilePath("prop.json"),
+ .int32Values = {0, 2},
+ }};
+
+ std::unique_ptr<JsonFakeValueGenerator> generator =
+ std::make_unique<JsonFakeValueGenerator>(request);
+ getHub()->registerGenerator(0, std::move(generator));
+
+ // wait for some time.
+ std::this_thread::sleep_for(std::chrono::milliseconds(100));
+
+ std::vector<VehiclePropValue> expectedValues = {
+ VehiclePropValue{
+ .areaId = 0,
+ .value.int32Values = {8},
+ .prop = 289408000,
+ },
+ VehiclePropValue{
+ .areaId = 0,
+ .value.int32Values = {4},
+ .prop = 289408000,
+ },
+ VehiclePropValue{
+ .areaId = 0,
+ .value.int32Values = {16},
+ .prop = 289408000,
+ },
+ VehiclePropValue{
+ .areaId = 0,
+ .value.int32Values = {10},
+ .prop = 289408000,
+ },
+ };
+
+ // We have two iterations.
+ for (size_t i = 0; i < 4; i++) {
+ expectedValues.push_back(expectedValues[i]);
+ }
+
+ auto events = getEvents();
+
+ long lastEventTime = currentTime;
+ for (auto& event : events) {
+ EXPECT_GT(event.timestamp, lastEventTime);
+ lastEventTime = event.timestamp;
+ event.timestamp = 0;
+ }
+
+ EXPECT_EQ(events, expectedValues);
+}
+
+TEST_F(FakeVehicleHalValueGeneratorsTest, testJsonFakeValueGeneratorInvalidFile) {
+ VehiclePropValue request = {.value = {
+ .stringValue = getTestFilePath("prop_invalid.json"),
+ .int32Values = {0, 2},
+ }};
+
+ std::unique_ptr<JsonFakeValueGenerator> generator =
+ std::make_unique<JsonFakeValueGenerator>(request);
+ getHub()->registerGenerator(0, std::move(generator));
+
+ // wait for some time.
+ std::this_thread::sleep_for(std::chrono::milliseconds(100));
+
+ ASSERT_TRUE(getEvents().empty());
+}
+
+TEST_F(FakeVehicleHalValueGeneratorsTest, testJsonFakeValueGeneratorNonExistingFile) {
+ VehiclePropValue request = {.value = {
+ .stringValue = "non_existing_file",
+ .int32Values = {0, 2},
+ }};
+
+ std::unique_ptr<JsonFakeValueGenerator> generator =
+ std::make_unique<JsonFakeValueGenerator>(request);
+ getHub()->registerGenerator(0, std::move(generator));
+
+ // wait for some time.
+ std::this_thread::sleep_for(std::chrono::milliseconds(100));
+
+ ASSERT_TRUE(getEvents().empty());
+}
+
+} // namespace fake
+} // namespace vehicle
+} // namespace automotive
+} // namespace hardware
+} // namespace android
diff --git a/automotive/vehicle/aidl/impl/fake_impl/GeneratorHub/test/prop.json b/automotive/vehicle/aidl/impl/fake_impl/GeneratorHub/test/prop.json
new file mode 100644
index 0000000..b881109
--- /dev/null
+++ b/automotive/vehicle/aidl/impl/fake_impl/GeneratorHub/test/prop.json
@@ -0,0 +1,26 @@
+[
+ {
+ "timestamp": 1000000,
+ "areaId": 0,
+ "value": 8,
+ "prop": 289408000
+ },
+ {
+ "timestamp": 2000000,
+ "areaId": 0,
+ "value": 4,
+ "prop": 289408000
+ },
+ {
+ "timestamp": 3000000,
+ "areaId": 0,
+ "value": 16,
+ "prop": 289408000
+ },
+ {
+ "timestamp": 4000000,
+ "areaId": 0,
+ "value": 10,
+ "prop": 289408000
+ }
+]
diff --git a/automotive/vehicle/aidl/impl/fake_impl/GeneratorHub/test/prop_invalid.json b/automotive/vehicle/aidl/impl/fake_impl/GeneratorHub/test/prop_invalid.json
new file mode 100644
index 0000000..98232c6
--- /dev/null
+++ b/automotive/vehicle/aidl/impl/fake_impl/GeneratorHub/test/prop_invalid.json
@@ -0,0 +1 @@
+{
diff --git a/automotive/vehicle/aidl/impl/fake_impl/hardware/Android.bp b/automotive/vehicle/aidl/impl/fake_impl/hardware/Android.bp
new file mode 100644
index 0000000..d614c6b
--- /dev/null
+++ b/automotive/vehicle/aidl/impl/fake_impl/hardware/Android.bp
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2021 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 {
+ default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+cc_library {
+ name: "FakeVehicleHardware",
+ vendor: true,
+ srcs: ["src/*.cpp"],
+ local_include_dirs: ["include"],
+ export_include_dirs: ["include"],
+ defaults: ["VehicleHalDefaults"],
+ header_libs: [
+ "IVehicleHardware",
+ "VehicleHalDefaultConfig",
+ ],
+ export_header_lib_headers: ["IVehicleHardware"],
+ static_libs: ["VehicleHalUtils"],
+ export_static_lib_headers: ["VehicleHalUtils"],
+}
diff --git a/automotive/vehicle/aidl/impl/fake_impl/hardware/include/FakeVehicleHardware.h b/automotive/vehicle/aidl/impl/fake_impl/hardware/include/FakeVehicleHardware.h
new file mode 100644
index 0000000..dee36f4
--- /dev/null
+++ b/automotive/vehicle/aidl/impl/fake_impl/hardware/include/FakeVehicleHardware.h
@@ -0,0 +1,104 @@
+/*
+ * Copyright (C) 2021 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_aidl_impl_fake_impl_hardware_include_FakeVehicleHardware_H_
+#define android_hardware_automotive_vehicle_aidl_impl_fake_impl_hardware_include_FakeVehicleHardware_H_
+
+#include <DefaultConfig.h>
+#include <IVehicleHardware.h>
+#include <VehicleHalTypes.h>
+#include <VehiclePropertyStore.h>
+#include <android-base/thread_annotations.h>
+
+#include <map>
+#include <memory>
+#include <mutex>
+#include <vector>
+
+namespace android {
+namespace hardware {
+namespace automotive {
+namespace vehicle {
+namespace fake {
+
+class FakeVehicleHardware final : public IVehicleHardware {
+ public:
+ using SetValuesCallback = std::function<void(
+ const std::vector<::aidl::android::hardware::automotive::vehicle::SetValueResult>&)>;
+ using GetValuesCallback = std::function<void(
+ const std::vector<::aidl::android::hardware::automotive::vehicle::GetValueResult>&)>;
+ using OnPropertyChangeCallback = std::function<void(
+ const std::vector<::aidl::android::hardware::automotive::vehicle::VehiclePropValue>&)>;
+ using OnPropertySetErrorCallback = std::function<void(const std::vector<SetValueErrorEvent>&)>;
+
+ FakeVehicleHardware();
+
+ explicit FakeVehicleHardware(std::unique_ptr<VehiclePropValuePool> valuePool);
+
+ // Get all the property configs.
+ std::vector<::aidl::android::hardware::automotive::vehicle::VehiclePropConfig>
+ getAllPropertyConfigs() const override;
+
+ // Set property values asynchronously. Server could return before the property set requests
+ // are sent to vehicle bus or before property set confirmation is received. The callback is
+ // safe to be called after the function returns and is safe to be called in a different thread.
+ ::aidl::android::hardware::automotive::vehicle::StatusCode setValues(
+ SetValuesCallback&& callback,
+ const std::vector<::aidl::android::hardware::automotive::vehicle::SetValueRequest>&
+ requests) override;
+
+ // Get property values asynchronously. Server could return before the property values are ready.
+ // The callback is safe to be called after the function returns and is safe to be called in a
+ // different thread.
+ ::aidl::android::hardware::automotive::vehicle::StatusCode getValues(
+ GetValuesCallback&& callback,
+ const std::vector<::aidl::android::hardware::automotive::vehicle::GetValueRequest>&
+ requests) const override;
+
+ // Dump debug information in the server.
+ DumpResult dump(const std::vector<std::string>& options) override;
+
+ // Check whether the system is healthy, return {@code StatusCode::OK} for healthy.
+ ::aidl::android::hardware::automotive::vehicle::StatusCode checkHealth() override;
+
+ // Register a callback that would be called when there is a property change event from vehicle.
+ void registerOnPropertyChangeEvent(OnPropertyChangeCallback&& callback) override;
+
+ // Register a callback that would be called when there is a property set error event from
+ // vehicle.
+ void registerOnPropertySetErrorEvent(OnPropertySetErrorCallback&& callback) override;
+
+ private:
+ void storePropInitialValue(const defaultconfig::ConfigDeclaration& config);
+ void init(std::shared_ptr<VehiclePropValuePool> valuePool);
+ void onValueChangeCallback(
+ const ::aidl::android::hardware::automotive::vehicle::VehiclePropValue& value);
+
+ std::unique_ptr<VehiclePropertyStore> mServerSidePropStore;
+ // mValuePool is also used in mServerSidePropStore.
+ std::shared_ptr<VehiclePropValuePool> mValuePool;
+ std::mutex mCallbackLock;
+ OnPropertyChangeCallback mOnPropertyChangeCallback GUARDED_BY(mCallbackLock);
+ OnPropertySetErrorCallback mOnPropertySetErrorCallback GUARDED_BY(mCallbackLock);
+};
+
+} // namespace fake
+} // namespace vehicle
+} // namespace automotive
+} // namespace hardware
+} // namespace android
+
+#endif // android_hardware_automotive_vehicle_aidl_impl_fake_impl_hardware_include_FakeVehicleHardware_H_
diff --git a/automotive/vehicle/aidl/impl/fake_impl/hardware/src/FakeVehicleHardware.cpp b/automotive/vehicle/aidl/impl/fake_impl/hardware/src/FakeVehicleHardware.cpp
new file mode 100644
index 0000000..f8bf7de
--- /dev/null
+++ b/automotive/vehicle/aidl/impl/fake_impl/hardware/src/FakeVehicleHardware.cpp
@@ -0,0 +1,208 @@
+/*
+ * Copyright (C) 2021 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 "FakeVehicleHardware.h"
+
+#include <DefaultConfig.h>
+#include <VehicleHalTypes.h>
+#include <VehicleUtils.h>
+#include <utils/Log.h>
+#include <utils/SystemClock.h>
+
+#include <vector>
+
+namespace android {
+namespace hardware {
+namespace automotive {
+namespace vehicle {
+namespace fake {
+
+using ::aidl::android::hardware::automotive::vehicle::GetValueRequest;
+using ::aidl::android::hardware::automotive::vehicle::GetValueResult;
+using ::aidl::android::hardware::automotive::vehicle::RawPropValues;
+using ::aidl::android::hardware::automotive::vehicle::SetValueRequest;
+using ::aidl::android::hardware::automotive::vehicle::SetValueResult;
+using ::aidl::android::hardware::automotive::vehicle::StatusCode;
+using ::aidl::android::hardware::automotive::vehicle::VehiclePropConfig;
+using ::aidl::android::hardware::automotive::vehicle::VehiclePropertyStatus;
+using ::aidl::android::hardware::automotive::vehicle::VehiclePropValue;
+
+void FakeVehicleHardware::storePropInitialValue(const defaultconfig::ConfigDeclaration& config) {
+ const VehiclePropConfig& vehiclePropConfig = config.config;
+ int propId = vehiclePropConfig.prop;
+
+ // A global property will have only a single area
+ bool globalProp = isGlobalProp(propId);
+ size_t numAreas = globalProp ? 1 : vehiclePropConfig.areaConfigs.size();
+
+ for (size_t i = 0; i < numAreas; i++) {
+ int32_t curArea = globalProp ? 0 : vehiclePropConfig.areaConfigs[i].areaId;
+
+ // Create a separate instance for each individual zone
+ VehiclePropValue prop = {
+ .areaId = curArea,
+ .prop = propId,
+ .timestamp = elapsedRealtimeNano(),
+ };
+
+ if (config.initialAreaValues.empty()) {
+ if (config.initialValue == RawPropValues{}) {
+ // Skip empty initial values.
+ continue;
+ }
+ prop.value = config.initialValue;
+ } else if (auto valueForAreaIt = config.initialAreaValues.find(curArea);
+ valueForAreaIt != config.initialAreaValues.end()) {
+ prop.value = valueForAreaIt->second;
+ } else {
+ ALOGW("failed to get default value for prop 0x%x area 0x%x", propId, curArea);
+ continue;
+ }
+
+ auto result =
+ mServerSidePropStore->writeValue(mValuePool->obtain(prop), /*updateStatus=*/true);
+ if (!result.ok()) {
+ ALOGE("failed to write default config value, error: %s",
+ result.error().message().c_str());
+ }
+ }
+}
+
+FakeVehicleHardware::FakeVehicleHardware() {
+ mValuePool = std::make_shared<VehiclePropValuePool>();
+ init(mValuePool);
+}
+
+FakeVehicleHardware::FakeVehicleHardware(std::unique_ptr<VehiclePropValuePool> valuePool)
+ : mValuePool(std::move(valuePool)) {
+ init(mValuePool);
+}
+
+void FakeVehicleHardware::init(std::shared_ptr<VehiclePropValuePool> valuePool) {
+ mServerSidePropStore.reset(new VehiclePropertyStore(valuePool));
+ for (auto& it : defaultconfig::getDefaultConfigs()) {
+ VehiclePropConfig cfg = it.config;
+ mServerSidePropStore->registerProperty(cfg);
+ storePropInitialValue(it);
+ }
+
+ mServerSidePropStore->setOnValueChangeCallback(
+ [this](const VehiclePropValue& value) { return onValueChangeCallback(value); });
+}
+
+std::vector<VehiclePropConfig> FakeVehicleHardware::getAllPropertyConfigs() const {
+ return mServerSidePropStore->getAllConfigs();
+}
+
+StatusCode FakeVehicleHardware::setValues(FakeVehicleHardware::SetValuesCallback&& callback,
+ const std::vector<SetValueRequest>& requests) {
+ std::vector<VehiclePropValue> updatedValues;
+ std::vector<SetValueResult> results;
+ for (auto& request : requests) {
+ const VehiclePropValue* value = &request.value;
+ ALOGD("setValues(%d)", value->prop);
+
+ auto updatedValue = mValuePool->obtain(*value);
+ int64_t timestamp = elapsedRealtimeNano();
+ updatedValue->timestamp = timestamp;
+
+ auto writeResult = mServerSidePropStore->writeValue(std::move(updatedValue));
+ SetValueResult setValueResult;
+ setValueResult.requestId = request.requestId;
+ if (!writeResult.ok()) {
+ ALOGE("failed to write value into property store, error: %s, code: %d",
+ writeResult.error().message().c_str(), writeResult.error().code());
+ setValueResult.status = StatusCode::INVALID_ARG;
+ } else {
+ setValueResult.status = StatusCode::OK;
+ }
+ results.push_back(std::move(setValueResult));
+ }
+
+ // In the real vhal, the values will be sent to Car ECU. We just pretend it is done here and
+ // send back the updated property values to client.
+ callback(std::move(results));
+
+ return StatusCode::OK;
+}
+
+StatusCode FakeVehicleHardware::getValues(FakeVehicleHardware::GetValuesCallback&& callback,
+ const std::vector<GetValueRequest>& requests) const {
+ std::vector<GetValueResult> results;
+ for (auto& request : requests) {
+ const VehiclePropValue* value = &request.prop;
+ ALOGD("getValues(%d)", value->prop);
+
+ auto readResult = mServerSidePropStore->readValue(*value);
+ GetValueResult getValueResult;
+ getValueResult.requestId = request.requestId;
+ if (!readResult.ok()) {
+ auto error = readResult.error();
+ if (error.code() == toInt(StatusCode::NOT_AVAILABLE)) {
+ ALOGW("%s", "value has not been set yet");
+ getValueResult.status = StatusCode::NOT_AVAILABLE;
+ } else {
+ ALOGE("failed to get value, error: %s, code: %d", error.message().c_str(),
+ error.code());
+ getValueResult.status = StatusCode::INVALID_ARG;
+ }
+ } else {
+ getValueResult.status = StatusCode::OK;
+ getValueResult.prop = *readResult.value();
+ }
+ results.push_back(std::move(getValueResult));
+ }
+
+ callback(std::move(results));
+
+ return StatusCode::OK;
+}
+
+DumpResult FakeVehicleHardware::dump(const std::vector<std::string>&) {
+ DumpResult result;
+ // TODO(b/201830716): Implement this.
+ return result;
+}
+
+StatusCode FakeVehicleHardware::checkHealth() {
+ // TODO(b/201830716): Implement this.
+ return StatusCode::OK;
+}
+
+void FakeVehicleHardware::registerOnPropertyChangeEvent(OnPropertyChangeCallback&& callback) {
+ std::scoped_lock<std::mutex> lockGuard(mCallbackLock);
+ mOnPropertyChangeCallback = std::move(callback);
+}
+
+void FakeVehicleHardware::registerOnPropertySetErrorEvent(OnPropertySetErrorCallback&& callback) {
+ std::scoped_lock<std::mutex> lockGuard(mCallbackLock);
+ mOnPropertySetErrorCallback = std::move(callback);
+}
+
+void FakeVehicleHardware::onValueChangeCallback(const VehiclePropValue& value) {
+ std::scoped_lock<std::mutex> lockGuard(mCallbackLock);
+ if (mOnPropertyChangeCallback != nullptr) {
+ std::vector<VehiclePropValue> updatedValues;
+ updatedValues.push_back(value);
+ mOnPropertyChangeCallback(std::move(updatedValues));
+ }
+}
+
+} // namespace fake
+} // namespace vehicle
+} // namespace automotive
+} // namespace hardware
+} // namespace android
diff --git a/automotive/vehicle/aidl/impl/fake_impl/hardware/test/Android.bp b/automotive/vehicle/aidl/impl/fake_impl/hardware/test/Android.bp
new file mode 100644
index 0000000..9f76d09
--- /dev/null
+++ b/automotive/vehicle/aidl/impl/fake_impl/hardware/test/Android.bp
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2021 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 {
+ default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+cc_test {
+ name: "FakeVehicleHardwareTest",
+ vendor: true,
+ srcs: ["*.cpp"],
+ header_libs: [
+ "IVehicleHardware",
+ "VehicleHalDefaultConfig",
+ ],
+ static_libs: [
+ "VehicleHalUtils",
+ "FakeVehicleHardware",
+ "libgtest",
+ "libgmock",
+ ],
+ defaults: ["VehicleHalDefaults"],
+ test_suites: ["device-tests"],
+}
diff --git a/automotive/vehicle/aidl/impl/fake_impl/hardware/test/FakeVehicleHardwareTest.cpp b/automotive/vehicle/aidl/impl/fake_impl/hardware/test/FakeVehicleHardwareTest.cpp
new file mode 100644
index 0000000..53e647d
--- /dev/null
+++ b/automotive/vehicle/aidl/impl/fake_impl/hardware/test/FakeVehicleHardwareTest.cpp
@@ -0,0 +1,431 @@
+/*
+ * Copyright (C) 2021 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 <DefaultConfig.h>
+#include <FakeVehicleHardware.h>
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+#include <utils/SystemClock.h>
+
+namespace android {
+namespace hardware {
+namespace automotive {
+namespace vehicle {
+namespace fake {
+
+namespace {
+
+using ::aidl::android::hardware::automotive::vehicle::GetValueRequest;
+using ::aidl::android::hardware::automotive::vehicle::GetValueResult;
+using ::aidl::android::hardware::automotive::vehicle::RawPropValues;
+using ::aidl::android::hardware::automotive::vehicle::SetValueRequest;
+using ::aidl::android::hardware::automotive::vehicle::SetValueResult;
+using ::aidl::android::hardware::automotive::vehicle::StatusCode;
+using ::aidl::android::hardware::automotive::vehicle::VehiclePropConfig;
+using ::aidl::android::hardware::automotive::vehicle::VehicleProperty;
+using ::aidl::android::hardware::automotive::vehicle::VehiclePropertyStatus;
+using ::aidl::android::hardware::automotive::vehicle::VehiclePropValue;
+using ::testing::ContainerEq;
+using ::testing::Eq;
+using ::testing::WhenSortedBy;
+
+constexpr int INVALID_PROP_ID = 0;
+
+} // namespace
+
+class FakeVehicleHardwareTest : public ::testing::Test {
+ protected:
+ void SetUp() override {}
+
+ FakeVehicleHardware* getHardware() { return &mHardware; }
+
+ StatusCode setValues(const std::vector<SetValueRequest>& requests) {
+ return getHardware()->setValues(
+ [this](const std::vector<SetValueResult> results) { return onSetValues(results); },
+ requests);
+ }
+
+ StatusCode getValues(const std::vector<GetValueRequest>& requests) {
+ return getHardware()->getValues(
+ [this](const std::vector<GetValueResult> results) { return onGetValues(results); },
+ requests);
+ }
+
+ void onSetValues(const std::vector<SetValueResult> results) {
+ for (auto& result : results) {
+ mSetValueResults.push_back(result);
+ }
+ }
+
+ const std::vector<SetValueResult>& getSetValueResults() { return mSetValueResults; }
+
+ void onGetValues(const std::vector<GetValueResult> results) {
+ for (auto& result : results) {
+ mGetValueResults.push_back(result);
+ }
+ }
+
+ const std::vector<GetValueResult>& getGetValueResults() { return mGetValueResults; }
+
+ void onPropertyChangeEvent(const std::vector<VehiclePropValue>& values) {
+ for (auto& value : values) {
+ mChangedProperties.push_back(value);
+ }
+ }
+
+ const std::vector<VehiclePropValue>& getChangedProperties() { return mChangedProperties; }
+
+ static void addSetValueRequest(std::vector<SetValueRequest>& requests,
+ std::vector<SetValueResult>& expectedResults, int64_t requestId,
+ const VehiclePropValue& value, StatusCode expectedStatus) {
+ SetValueRequest request;
+ request.requestId = requestId;
+ request.value = value;
+ request.value.timestamp = elapsedRealtimeNano();
+ requests.push_back(std::move(request));
+
+ SetValueResult result;
+ result.requestId = requestId;
+ result.status = expectedStatus;
+ expectedResults.push_back(std::move(result));
+ }
+
+ static void addGetValueRequest(std::vector<GetValueRequest>& requests,
+ std::vector<GetValueResult>& expectedResults, int64_t requestId,
+ const VehiclePropValue& value, StatusCode expectedStatus) {
+ GetValueRequest request;
+ request.requestId = requestId;
+ request.prop.prop = value.prop;
+ request.prop.areaId = value.areaId;
+ requests.push_back(std::move(request));
+
+ GetValueResult result;
+ result.requestId = requestId;
+ result.status = expectedStatus;
+ if (expectedStatus == StatusCode::OK) {
+ result.prop = value;
+ }
+ expectedResults.push_back(std::move(result));
+ }
+
+ std::vector<VehiclePropValue> getTestPropValues() {
+ VehiclePropValue fuelCapacity = {
+ .prop = toInt(VehicleProperty::INFO_FUEL_CAPACITY),
+ .value = {.floatValues = {1.0}},
+ };
+
+ VehiclePropValue leftTirePressure = {
+ .prop = toInt(VehicleProperty::TIRE_PRESSURE),
+ .value = {.floatValues = {170.0}},
+ .areaId = WHEEL_FRONT_LEFT,
+ };
+
+ VehiclePropValue rightTirePressure = {
+ .prop = toInt(VehicleProperty::TIRE_PRESSURE),
+ .value = {.floatValues = {180.0}},
+ .areaId = WHEEL_FRONT_RIGHT,
+ };
+
+ return {fuelCapacity, leftTirePressure, rightTirePressure};
+ }
+
+ struct PropValueCmp {
+ bool operator()(const VehiclePropValue& a, const VehiclePropValue& b) const {
+ return (a.prop < b.prop) || ((a.prop == b.prop) && (a.value < b.value)) ||
+ ((a.prop == b.prop) && (a.value == b.value) && (a.areaId < b.areaId));
+ }
+ } mPropValueCmp;
+
+ private:
+ FakeVehicleHardware mHardware;
+ std::vector<SetValueResult> mSetValueResults;
+ std::vector<GetValueResult> mGetValueResults;
+ std::vector<VehiclePropValue> mChangedProperties;
+};
+
+TEST_F(FakeVehicleHardwareTest, testGetAllPropertyConfigs) {
+ std::vector<VehiclePropConfig> configs = getHardware()->getAllPropertyConfigs();
+
+ ASSERT_EQ(configs.size(), defaultconfig::getDefaultConfigs().size());
+}
+
+TEST_F(FakeVehicleHardwareTest, testGetDefaultValues) {
+ std::vector<GetValueRequest> getValueRequests;
+ std::vector<GetValueResult> expectedGetValueResults;
+ int64_t requestId = 1;
+
+ for (auto& config : defaultconfig::getDefaultConfigs()) {
+ int propId = config.config.prop;
+ if (isGlobalProp(propId)) {
+ if (config.initialValue == RawPropValues{}) {
+ addGetValueRequest(getValueRequests, expectedGetValueResults, requestId++,
+ VehiclePropValue{.prop = propId}, StatusCode::NOT_AVAILABLE);
+ continue;
+ }
+ addGetValueRequest(getValueRequests, expectedGetValueResults, requestId++,
+ VehiclePropValue{
+ .prop = propId,
+ .value = config.initialValue,
+ },
+ StatusCode::OK);
+ continue;
+ }
+ for (auto areaConfig : config.config.areaConfigs) {
+ StatusCode status = StatusCode::OK;
+ VehiclePropValue propValue{
+ .prop = propId,
+ .areaId = areaConfig.areaId,
+ };
+ if (config.initialAreaValues.empty()) {
+ if (config.initialValue == RawPropValues{}) {
+ status = StatusCode::NOT_AVAILABLE;
+ } else {
+ propValue.value = config.initialValue;
+ }
+ } else if (auto valueForAreaIt = config.initialAreaValues.find(areaConfig.areaId);
+ valueForAreaIt != config.initialAreaValues.end()) {
+ propValue.value = valueForAreaIt->second;
+ } else {
+ status = StatusCode::NOT_AVAILABLE;
+ }
+ addGetValueRequest(getValueRequests, expectedGetValueResults, requestId++, propValue,
+ status);
+ }
+ }
+
+ // In our implementation, this would finish immediately.
+ StatusCode status = getValues(getValueRequests);
+
+ ASSERT_EQ(status, StatusCode::OK);
+
+ std::vector<GetValueResult> getValueResultsWithNoTimestamp;
+ for (auto& result : getGetValueResults()) {
+ GetValueResult resultCopy = result;
+ resultCopy.prop->timestamp = 0;
+ getValueResultsWithNoTimestamp.push_back(std::move(resultCopy));
+ }
+ ASSERT_THAT(getValueResultsWithNoTimestamp, ContainerEq(expectedGetValueResults));
+}
+
+TEST_F(FakeVehicleHardwareTest, testSetValues) {
+ std::vector<SetValueRequest> requests;
+ std::vector<SetValueResult> expectedResults;
+
+ int64_t requestId = 1;
+ for (auto& value : getTestPropValues()) {
+ addSetValueRequest(requests, expectedResults, requestId++, value, StatusCode::OK);
+ }
+
+ StatusCode status = setValues(requests);
+
+ ASSERT_EQ(status, StatusCode::OK);
+
+ // Although callback might be called asynchronously, in our implementation, the callback would
+ // be called before setValues returns.
+ ASSERT_THAT(getSetValueResults(), ContainerEq(expectedResults));
+}
+
+TEST_F(FakeVehicleHardwareTest, testSetValuesError) {
+ std::vector<SetValueRequest> requests;
+ std::vector<SetValueResult> expectedResults;
+
+ int64_t requestId = 1;
+
+ VehiclePropValue invalidProp = {
+ .prop = INVALID_PROP_ID,
+ };
+ addSetValueRequest(requests, expectedResults, requestId++, invalidProp,
+ StatusCode::INVALID_ARG);
+
+ for (auto& value : getTestPropValues()) {
+ addSetValueRequest(requests, expectedResults, requestId++, value, StatusCode::OK);
+ }
+
+ StatusCode status = setValues(requests);
+
+ ASSERT_EQ(status, StatusCode::OK);
+
+ // Although callback might be called asynchronously, in our implementation, the callback would
+ // be called before setValues returns.
+ ASSERT_THAT(getSetValueResults(), ContainerEq(expectedResults));
+}
+
+TEST_F(FakeVehicleHardwareTest, testRegisterOnPropertyChangeEvent) {
+ getHardware()->registerOnPropertyChangeEvent(std::bind(
+ &FakeVehicleHardwareTest_testRegisterOnPropertyChangeEvent_Test::onPropertyChangeEvent,
+ this, std::placeholders::_1));
+
+ auto testValues = getTestPropValues();
+ std::vector<SetValueRequest> requests;
+ std::vector<SetValueResult> expectedResults;
+ int64_t requestId = 1;
+ for (auto& value : testValues) {
+ addSetValueRequest(requests, expectedResults, requestId++, value, StatusCode::OK);
+ }
+ int64_t timestamp = elapsedRealtimeNano();
+
+ StatusCode status = setValues(requests);
+
+ ASSERT_EQ(status, StatusCode::OK);
+
+ auto updatedValues = getChangedProperties();
+ std::vector<VehiclePropValue> updatedValuesWithNoTimestamp;
+ for (auto& value : updatedValues) {
+ ASSERT_GE(value.timestamp, timestamp);
+ VehiclePropValue valueCopy = value;
+ valueCopy.timestamp = 0;
+ updatedValuesWithNoTimestamp.push_back(std::move(valueCopy));
+ }
+
+ ASSERT_THAT(updatedValuesWithNoTimestamp, WhenSortedBy(mPropValueCmp, Eq(testValues)));
+}
+
+TEST_F(FakeVehicleHardwareTest, testReadValues) {
+ std::vector<SetValueRequest> setValueRequests;
+ std::vector<SetValueResult> expectedSetValueResults;
+
+ int64_t requestId = 1;
+ for (auto& value : getTestPropValues()) {
+ addSetValueRequest(setValueRequests, expectedSetValueResults, requestId++, value,
+ StatusCode::OK);
+ }
+ int64_t timestamp = elapsedRealtimeNano();
+
+ // In our implementation, this would finish immediately.
+ StatusCode status = setValues(setValueRequests);
+
+ ASSERT_EQ(status, StatusCode::OK);
+
+ std::vector<GetValueRequest> getValueRequests;
+ std::vector<GetValueResult> expectedGetValueResults;
+ for (auto& value : getTestPropValues()) {
+ addGetValueRequest(getValueRequests, expectedGetValueResults, requestId++, value,
+ StatusCode::OK);
+ }
+
+ // In our implementation, this would finish immediately.
+ status = getValues(getValueRequests);
+
+ ASSERT_EQ(status, StatusCode::OK);
+
+ std::vector<GetValueResult> getValueResultsWithNoTimestamp;
+ for (auto& result : getGetValueResults()) {
+ ASSERT_GE(result.prop->timestamp, timestamp);
+ GetValueResult resultCopy = result;
+ resultCopy.prop->timestamp = 0;
+ getValueResultsWithNoTimestamp.push_back(std::move(resultCopy));
+ }
+ ASSERT_THAT(getValueResultsWithNoTimestamp, ContainerEq(expectedGetValueResults));
+}
+
+TEST_F(FakeVehicleHardwareTest, testReadValuesErrorInvalidProp) {
+ std::vector<SetValueRequest> setValueRequests;
+ std::vector<SetValueResult> expectedSetValueResults;
+
+ int64_t requestId = 1;
+ for (auto& value : getTestPropValues()) {
+ addSetValueRequest(setValueRequests, expectedSetValueResults, requestId++, value,
+ StatusCode::OK);
+ }
+
+ // In our implementation, this would finish immediately.
+ StatusCode status = setValues(setValueRequests);
+
+ ASSERT_EQ(status, StatusCode::OK);
+
+ std::vector<GetValueRequest> getValueRequests;
+ std::vector<GetValueResult> expectedGetValueResults;
+ VehiclePropValue invalidProp = {
+ .prop = INVALID_PROP_ID,
+ };
+ addGetValueRequest(getValueRequests, expectedGetValueResults, requestId++, invalidProp,
+ StatusCode::INVALID_ARG);
+
+ // In our implementation, this would finish immediately.
+ status = getValues(getValueRequests);
+
+ ASSERT_EQ(status, StatusCode::OK);
+ ASSERT_THAT(getGetValueResults(), ContainerEq(expectedGetValueResults));
+}
+
+TEST_F(FakeVehicleHardwareTest, testReadValuesErrorNotAvailable) {
+ std::vector<GetValueRequest> getValueRequests;
+ std::vector<GetValueResult> expectedGetValueResults;
+ // VEHICLE_MAP_SERVICE does not have initial value, 'get' must always return
+ // StatusCode::NOT_AVAILABLE.
+ addGetValueRequest(getValueRequests, expectedGetValueResults, 0,
+ VehiclePropValue{
+ .prop = VEHICLE_MAP_SERVICE,
+ },
+ StatusCode::NOT_AVAILABLE);
+
+ // In our implementation, this would finish immediately.
+ StatusCode status = getValues(getValueRequests);
+
+ ASSERT_EQ(status, StatusCode::OK);
+ ASSERT_THAT(getGetValueResults(), ContainerEq(expectedGetValueResults));
+}
+
+TEST_F(FakeVehicleHardwareTest, testSetStatusMustIgnore) {
+ VehiclePropValue testValue = getTestPropValues()[0];
+ testValue.status = VehiclePropertyStatus::UNAVAILABLE;
+
+ std::vector<SetValueRequest> setValueRequests;
+ std::vector<SetValueResult> expectedSetValueResults;
+
+ int64_t requestId = 1;
+ addSetValueRequest(setValueRequests, expectedSetValueResults, requestId++, testValue,
+ StatusCode::OK);
+
+ // In our implementation, this would finish immediately.
+ StatusCode status = setValues(setValueRequests);
+
+ ASSERT_EQ(status, StatusCode::OK);
+ ASSERT_THAT(getSetValueResults(), ContainerEq(expectedSetValueResults));
+
+ std::vector<GetValueRequest> getValueRequests;
+ getValueRequests.push_back(GetValueRequest{
+ .requestId = requestId++,
+ .prop = testValue,
+ });
+
+ // In our implementation, this would finish immediately.
+ status = getValues(getValueRequests);
+
+ ASSERT_EQ(status, StatusCode::OK);
+ ASSERT_EQ(getGetValueResults().size(), static_cast<size_t>(1));
+ ASSERT_EQ(getGetValueResults()[0].status, StatusCode::OK);
+ // The status should be by-default AVAILABLE for new status.
+ ASSERT_EQ(getGetValueResults()[0].prop->status, VehiclePropertyStatus::AVAILABLE);
+
+ // Try to set the property again. The status should not be overwritten.
+ status = setValues(setValueRequests);
+
+ ASSERT_EQ(status, StatusCode::OK);
+
+ status = getValues(getValueRequests);
+
+ ASSERT_EQ(status, StatusCode::OK);
+ ASSERT_EQ(getGetValueResults().size(), static_cast<size_t>(2));
+ ASSERT_EQ(getGetValueResults()[1].status, StatusCode::OK);
+ ASSERT_EQ(getGetValueResults()[1].prop->status, VehiclePropertyStatus::AVAILABLE);
+}
+
+} // namespace fake
+} // namespace vehicle
+} // namespace automotive
+} // namespace hardware
+} // namespace android
diff --git a/automotive/vehicle/aidl/impl/hardware/include/IVehicleHardware.h b/automotive/vehicle/aidl/impl/hardware/include/IVehicleHardware.h
index 58531b9..2e12327 100644
--- a/automotive/vehicle/aidl/impl/hardware/include/IVehicleHardware.h
+++ b/automotive/vehicle/aidl/impl/hardware/include/IVehicleHardware.h
@@ -36,14 +36,6 @@
std::string buffer;
};
-// A request type for 'setValues' or 'getValues' method.
-struct VehiclePropValueRequest {
- // A unique request ID set by the sender.
- int64_t requestId;
- // The property to get/set.
- ::aidl::android::hardware::automotive::vehicle::VehiclePropValue value;
-};
-
// A structure to represent a set value error event reported from vehicle.
struct SetValueErrorEvent {
::aidl::android::hardware::automotive::vehicle::StatusCode errorCode;
@@ -70,7 +62,8 @@
std::function<void(const std::vector<
::aidl::android::hardware::automotive::vehicle::SetValueResult>&)>&&
callback,
- const std::vector<VehiclePropValueRequest>& requests) = 0;
+ const std::vector<::aidl::android::hardware::automotive::vehicle::SetValueRequest>&
+ requests) = 0;
// Get property values asynchronously. Server could return before the property values are ready.
// The callback is safe to be called after the function returns and is safe to be called in a
@@ -79,7 +72,8 @@
std::function<void(const std::vector<
::aidl::android::hardware::automotive::vehicle::GetValueResult>&)>&&
callback,
- const std::vector<VehiclePropValueRequest>& requests) const = 0;
+ const std::vector<::aidl::android::hardware::automotive::vehicle::GetValueRequest>&
+ requests) const = 0;
// Dump debug information in the server.
virtual DumpResult dump(const std::vector<std::string>& options) = 0;
diff --git a/automotive/vehicle/aidl/impl/utils/common/include/ConcurrentQueue.h b/automotive/vehicle/aidl/impl/utils/common/include/ConcurrentQueue.h
new file mode 100644
index 0000000..9a8f19b
--- /dev/null
+++ b/automotive/vehicle/aidl/impl/utils/common/include/ConcurrentQueue.h
@@ -0,0 +1,99 @@
+/*
+ * Copyright (C) 2016 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_aidl_impl_utils_common_include_ConcurrentQueue_H_
+#define android_hardware_automotive_vehicle_aidl_impl_utils_common_include_ConcurrentQueue_H_
+
+#include <android-base/thread_annotations.h>
+
+#include <atomic>
+#include <condition_variable>
+#include <iostream>
+#include <queue>
+#include <thread>
+
+namespace android {
+namespace hardware {
+namespace automotive {
+namespace vehicle {
+
+template <typename T>
+class ConcurrentQueue {
+ public:
+ void waitForItems() {
+ std::unique_lock<std::mutex> lockGuard(mLock);
+ ::android::base::ScopedLockAssertion lockAssertion(mLock);
+ while (mQueue.empty() && mIsActive) {
+ mCond.wait(lockGuard);
+ }
+ }
+
+ std::vector<T> flush() {
+ std::vector<T> items;
+
+ std::scoped_lock<std::mutex> lockGuard(mLock);
+ if (mQueue.empty()) {
+ return items;
+ }
+ while (!mQueue.empty()) {
+ // Even if the queue is deactivated, we should still flush all the remaining values
+ // in the queue.
+ items.push_back(std::move(mQueue.front()));
+ mQueue.pop();
+ }
+ return items;
+ }
+
+ void push(T&& item) {
+ {
+ std::scoped_lock<std::mutex> lockGuard(mLock);
+ if (!mIsActive) {
+ return;
+ }
+ mQueue.push(std::move(item));
+ }
+ mCond.notify_one();
+ }
+
+ // Deactivates the queue, thus no one can push items to it, also notifies all waiting thread.
+ // The items already in the queue could still be flushed even after the queue is deactivated.
+ void deactivate() {
+ {
+ std::scoped_lock<std::mutex> lockGuard(mLock);
+ mIsActive = false;
+ }
+ // To unblock all waiting consumers.
+ mCond.notify_all();
+ }
+
+ ConcurrentQueue() = default;
+
+ ConcurrentQueue(const ConcurrentQueue&) = delete;
+ ConcurrentQueue& operator=(const ConcurrentQueue&) = delete;
+
+ private:
+ mutable std::mutex mLock;
+ bool mIsActive GUARDED_BY(mLock) = true;
+ std::condition_variable mCond;
+ std::queue<T> mQueue GUARDED_BY(mLock);
+};
+
+} // namespace vehicle
+} // namespace automotive
+} // namespace hardware
+} // namespace android
+
+#endif // android_hardware_automotive_vehicle_aidl_impl_utils_common_include_ConcurrentQueue_H_
diff --git a/automotive/vehicle/aidl/impl/utils/common/include/VehicleHalTypes.h b/automotive/vehicle/aidl/impl/utils/common/include/VehicleHalTypes.h
index 99f6431..9da2b14 100644
--- a/automotive/vehicle/aidl/impl/utils/common/include/VehicleHalTypes.h
+++ b/automotive/vehicle/aidl/impl/utils/common/include/VehicleHalTypes.h
@@ -17,12 +17,16 @@
#ifndef android_hardware_automotive_vehicle_aidl_impl_utils_common_include_VehicleHalTypes_H_
#define android_hardware_automotive_vehicle_aidl_impl_utils_common_include_VehicleHalTypes_H_
+#include <aidl/android/hardware/automotive/vehicle/DiagnosticFloatSensorIndex.h>
+#include <aidl/android/hardware/automotive/vehicle/DiagnosticIntegerSensorIndex.h>
#include <aidl/android/hardware/automotive/vehicle/EvConnectorType.h>
#include <aidl/android/hardware/automotive/vehicle/EvsServiceState.h>
#include <aidl/android/hardware/automotive/vehicle/EvsServiceType.h>
#include <aidl/android/hardware/automotive/vehicle/FuelType.h>
+#include <aidl/android/hardware/automotive/vehicle/GetValueRequest.h>
#include <aidl/android/hardware/automotive/vehicle/GetValueResult.h>
#include <aidl/android/hardware/automotive/vehicle/PortLocationType.h>
+#include <aidl/android/hardware/automotive/vehicle/SetValueRequest.h>
#include <aidl/android/hardware/automotive/vehicle/SetValueResult.h>
#include <aidl/android/hardware/automotive/vehicle/StatusCode.h>
#include <aidl/android/hardware/automotive/vehicle/VehicleApPowerStateReport.h>
diff --git a/automotive/vehicle/aidl/impl/utils/common/include/VehicleObjectPool.h b/automotive/vehicle/aidl/impl/utils/common/include/VehicleObjectPool.h
index 61e475a..4b2a11a 100644
--- a/automotive/vehicle/aidl/impl/utils/common/include/VehicleObjectPool.h
+++ b/automotive/vehicle/aidl/impl/utils/common/include/VehicleObjectPool.h
@@ -80,7 +80,7 @@
virtual ~ObjectPool() = default;
virtual recyclable_ptr<T> obtain() {
- std::lock_guard<std::mutex> lock(mLock);
+ std::scoped_lock<std::mutex> lock(mLock);
INC_METRIC_IF_DEBUG(Obtained)
if (mObjects.empty()) {
INC_METRIC_IF_DEBUG(Created)
@@ -100,7 +100,7 @@
virtual T* createObject() = 0;
virtual void recycle(T* o) {
- std::lock_guard<std::mutex> lock(mLock);
+ std::scoped_lock<std::mutex> lock(mLock);
size_t objectSize = mGetSizeFunc(*o);
if (objectSize > mMaxPoolObjectsSize ||
diff --git a/automotive/vehicle/aidl/impl/utils/common/include/VehiclePropertyStore.h b/automotive/vehicle/aidl/impl/utils/common/include/VehiclePropertyStore.h
index ababf5e..dcc7031 100644
--- a/automotive/vehicle/aidl/impl/utils/common/include/VehiclePropertyStore.h
+++ b/automotive/vehicle/aidl/impl/utils/common/include/VehiclePropertyStore.h
@@ -40,11 +40,17 @@
// to get value for all areas for particular property.
//
// This class is thread-safe, however it uses blocking synchronization across all methods.
-class VehiclePropertyStore {
+class VehiclePropertyStore final {
public:
explicit VehiclePropertyStore(std::shared_ptr<VehiclePropValuePool> valuePool)
: mValuePool(valuePool) {}
+ ~VehiclePropertyStore();
+
+ // Callback when a property value has been updated or a new value added.
+ using OnValueChangeCallback = std::function<void(
+ const ::aidl::android::hardware::automotive::vehicle::VehiclePropValue&)>;
+
// Function that used to calculate unique token for given VehiclePropValue.
using TokenFunction = ::std::function<int64_t(
const ::aidl::android::hardware::automotive::vehicle::VehiclePropValue& value)>;
@@ -80,11 +86,15 @@
::android::base::Result<std::vector<VehiclePropValuePool::RecyclableType>>
readValuesForProperty(int32_t propId) const;
- // Read the value for the requested property.
+ // Read the value for the requested property. Returns {@code StatusCode::NOT_AVAILABLE} if the
+ // value has not been set yet. Returns {@code StatusCode::INVALID_ARG} if the property is
+ // not configured.
::android::base::Result<VehiclePropValuePool::RecyclableType> readValue(
const ::aidl::android::hardware::automotive::vehicle::VehiclePropValue& request) const;
- // Read the value for the requested property.
+ // Read the value for the requested property. Returns {@code StatusCode::NOT_AVAILABLE} if the
+ // value has not been set yet. Returns {@code StatusCode::INVALID_ARG} if the property is
+ // not configured.
::android::base::Result<VehiclePropValuePool::RecyclableType> readValue(
int32_t prop, int32_t area = 0, int64_t token = 0) const;
@@ -97,6 +107,9 @@
const ::aidl::android::hardware::automotive::vehicle::VehiclePropConfig*>
getConfig(int32_t propId) const;
+ // Set a callback that would be called when a property value has been updated.
+ void setOnValueChangeCallback(const OnValueChangeCallback& callback);
+
private:
struct RecordId {
int32_t area;
@@ -117,10 +130,11 @@
std::unordered_map<RecordId, VehiclePropValuePool::RecyclableType, RecordIdHash> values;
};
- mutable std::mutex mLock;
- std::unordered_map<int32_t, Record> mRecordsByPropId GUARDED_BY(mLock);
// {@code VehiclePropValuePool} is thread-safe.
std::shared_ptr<VehiclePropValuePool> mValuePool;
+ mutable std::mutex mLock;
+ std::unordered_map<int32_t, Record> mRecordsByPropId GUARDED_BY(mLock);
+ OnValueChangeCallback mOnValueChangeCallback GUARDED_BY(mLock);
const Record* getRecordLocked(int32_t propId) const;
diff --git a/automotive/vehicle/aidl/impl/utils/common/src/VehicleObjectPool.cpp b/automotive/vehicle/aidl/impl/utils/common/src/VehicleObjectPool.cpp
index 0ff58f7..1cdc461 100644
--- a/automotive/vehicle/aidl/impl/utils/common/src/VehicleObjectPool.cpp
+++ b/automotive/vehicle/aidl/impl/utils/common/src/VehicleObjectPool.cpp
@@ -101,7 +101,7 @@
VehiclePropValuePool::RecyclableType VehiclePropValuePool::obtainRecyclable(
VehiclePropertyType type, size_t vectorSize) {
- std::lock_guard<std::mutex> lock(mLock);
+ std::scoped_lock<std::mutex> lock(mLock);
assert(vectorSize > 0);
// VehiclePropertyType is not overlapping with vectorSize.
diff --git a/automotive/vehicle/aidl/impl/utils/common/src/VehiclePropertyStore.cpp b/automotive/vehicle/aidl/impl/utils/common/src/VehiclePropertyStore.cpp
index 2869d1d..1a79230 100644
--- a/automotive/vehicle/aidl/impl/utils/common/src/VehiclePropertyStore.cpp
+++ b/automotive/vehicle/aidl/impl/utils/common/src/VehiclePropertyStore.cpp
@@ -19,6 +19,7 @@
#include "VehiclePropertyStore.h"
+#include <VehicleHalTypes.h>
#include <VehicleUtils.h>
#include <android-base/format.h>
#include <math/HashCombine.h>
@@ -28,10 +29,12 @@
namespace automotive {
namespace vehicle {
+using ::aidl::android::hardware::automotive::vehicle::StatusCode;
using ::aidl::android::hardware::automotive::vehicle::VehicleAreaConfig;
using ::aidl::android::hardware::automotive::vehicle::VehiclePropConfig;
using ::aidl::android::hardware::automotive::vehicle::VehiclePropertyStatus;
using ::aidl::android::hardware::automotive::vehicle::VehiclePropValue;
+using ::android::base::Error;
using ::android::base::Result;
bool VehiclePropertyStore::RecordId::operator==(const VehiclePropertyStore::RecordId& other) const {
@@ -49,6 +52,14 @@
return res;
}
+VehiclePropertyStore::~VehiclePropertyStore() {
+ std::scoped_lock<std::mutex> lockGuard(mLock);
+
+ // Recycling record requires mValuePool, so need to recycle them before destroying mValuePool.
+ mRecordsByPropId.clear();
+ mValuePool.reset();
+}
+
const VehiclePropertyStore::Record* VehiclePropertyStore::getRecordLocked(int32_t propId) const
REQUIRES(mLock) {
auto RecordIt = mRecordsByPropId.find(propId);
@@ -75,16 +86,16 @@
Result<VehiclePropValuePool::RecyclableType> VehiclePropertyStore::readValueLocked(
const RecordId& recId, const Record& record) const REQUIRES(mLock) {
- auto it = record.values.find(recId);
- if (it == record.values.end()) {
- return Errorf("Record ID: {} is not found", recId.toString());
+ if (auto it = record.values.find(recId); it != record.values.end()) {
+ return mValuePool->obtain(*(it->second));
}
- return mValuePool->obtain(*(it->second));
+ return Error(toInt(StatusCode::NOT_AVAILABLE))
+ << "Record ID: " << recId.toString() << " is not found";
}
void VehiclePropertyStore::registerProperty(const VehiclePropConfig& config,
VehiclePropertyStore::TokenFunction tokenFunc) {
- std::lock_guard<std::mutex> g(mLock);
+ std::scoped_lock<std::mutex> g(mLock);
mRecordsByPropId[config.prop] = Record{
.propConfig = config,
@@ -94,45 +105,52 @@
Result<void> VehiclePropertyStore::writeValue(VehiclePropValuePool::RecyclableType propValue,
bool updateStatus) {
- std::lock_guard<std::mutex> g(mLock);
+ std::scoped_lock<std::mutex> g(mLock);
- VehiclePropertyStore::Record* record = getRecordLocked(propValue->prop);
+ int32_t propId = propValue->prop;
+
+ VehiclePropertyStore::Record* record = getRecordLocked(propId);
if (record == nullptr) {
- return Errorf("property: {:d} not registered", propValue->prop);
+ return Error(toInt(StatusCode::INVALID_ARG)) << "property: " << propId << " not registered";
}
- if (!isGlobalProp(propValue->prop) &&
- getAreaConfig(*propValue, record->propConfig) == nullptr) {
- return Errorf("no config for property: {:d} area: {:d}", propValue->prop,
- propValue->areaId);
+ if (!isGlobalProp(propId) && getAreaConfig(*propValue, record->propConfig) == nullptr) {
+ return Error(toInt(StatusCode::INVALID_ARG))
+ << "no config for property: " << propId << " area: " << propValue->areaId;
}
VehiclePropertyStore::RecordId recId = getRecordIdLocked(*propValue, *record);
- auto it = record->values.find(recId);
- if (it == record->values.end()) {
- record->values[recId] = std::move(propValue);
- if (!updateStatus) {
- record->values[recId]->status = VehiclePropertyStatus::AVAILABLE;
+ bool valueUpdated = true;
+ if (auto it = record->values.find(recId); it != record->values.end()) {
+ const VehiclePropValue* valueToUpdate = it->second.get();
+ int64_t oldTimestamp = valueToUpdate->timestamp;
+ VehiclePropertyStatus oldStatus = valueToUpdate->status;
+ // propValue is outdated and drops it.
+ if (oldTimestamp > propValue->timestamp) {
+ return Error(toInt(StatusCode::INVALID_ARG))
+ << "outdated timestamp: " << propValue->timestamp;
}
- return {};
- }
- const VehiclePropValue* valueToUpdate = it->second.get();
- long oldTimestamp = valueToUpdate->timestamp;
- VehiclePropertyStatus oldStatus = valueToUpdate->status;
- // propValue is outdated and drops it.
- if (oldTimestamp > propValue->timestamp) {
- return Errorf("outdated timestamp: {:d}", propValue->timestamp);
- }
- record->values[recId] = std::move(propValue);
- if (!updateStatus) {
- record->values[recId]->status = oldStatus;
+ if (!updateStatus) {
+ propValue->status = oldStatus;
+ }
+
+ valueUpdated = (valueToUpdate->value != propValue->value ||
+ valueToUpdate->status != propValue->status ||
+ valueToUpdate->prop != propValue->prop ||
+ valueToUpdate->areaId != propValue->areaId);
+ } else if (!updateStatus) {
+ propValue->status = VehiclePropertyStatus::AVAILABLE;
}
+ record->values[recId] = std::move(propValue);
+ if (valueUpdated && mOnValueChangeCallback != nullptr) {
+ mOnValueChangeCallback(*(record->values[recId]));
+ }
return {};
}
void VehiclePropertyStore::removeValue(const VehiclePropValue& propValue) {
- std::lock_guard<std::mutex> g(mLock);
+ std::scoped_lock<std::mutex> g(mLock);
VehiclePropertyStore::Record* record = getRecordLocked(propValue.prop);
if (record == nullptr) {
@@ -146,7 +164,7 @@
}
void VehiclePropertyStore::removeValuesForProperty(int32_t propId) {
- std::lock_guard<std::mutex> g(mLock);
+ std::scoped_lock<std::mutex> g(mLock);
VehiclePropertyStore::Record* record = getRecordLocked(propId);
if (record == nullptr) {
@@ -157,7 +175,7 @@
}
std::vector<VehiclePropValuePool::RecyclableType> VehiclePropertyStore::readAllValues() const {
- std::lock_guard<std::mutex> g(mLock);
+ std::scoped_lock<std::mutex> g(mLock);
std::vector<VehiclePropValuePool::RecyclableType> allValues;
@@ -172,13 +190,13 @@
Result<std::vector<VehiclePropValuePool::RecyclableType>>
VehiclePropertyStore::readValuesForProperty(int32_t propId) const {
- std::lock_guard<std::mutex> g(mLock);
+ std::scoped_lock<std::mutex> g(mLock);
std::vector<VehiclePropValuePool::RecyclableType> values;
const VehiclePropertyStore::Record* record = getRecordLocked(propId);
if (record == nullptr) {
- return Errorf("property: {:d} not registered", propId);
+ return Error(toInt(StatusCode::INVALID_ARG)) << "property: " << propId << " not registered";
}
for (auto const& [_, value] : record->values) {
@@ -189,11 +207,12 @@
Result<VehiclePropValuePool::RecyclableType> VehiclePropertyStore::readValue(
const VehiclePropValue& propValue) const {
- std::lock_guard<std::mutex> g(mLock);
+ std::scoped_lock<std::mutex> g(mLock);
- const VehiclePropertyStore::Record* record = getRecordLocked(propValue.prop);
+ int32_t propId = propValue.prop;
+ const VehiclePropertyStore::Record* record = getRecordLocked(propId);
if (record == nullptr) {
- return Errorf("property: {:d} not registered", propValue.prop);
+ return Error(toInt(StatusCode::INVALID_ARG)) << "property: " << propId << " not registered";
}
VehiclePropertyStore::RecordId recId = getRecordIdLocked(propValue, *record);
@@ -203,11 +222,11 @@
Result<VehiclePropValuePool::RecyclableType> VehiclePropertyStore::readValue(int32_t propId,
int32_t areaId,
int64_t token) const {
- std::lock_guard<std::mutex> g(mLock);
+ std::scoped_lock<std::mutex> g(mLock);
const VehiclePropertyStore::Record* record = getRecordLocked(propId);
if (record == nullptr) {
- return Errorf("property: {:d} not registered", propId);
+ return Error(toInt(StatusCode::INVALID_ARG)) << "property: " << propId << " not registered";
}
VehiclePropertyStore::RecordId recId{.area = isGlobalProp(propId) ? 0 : areaId, .token = token};
@@ -215,7 +234,7 @@
}
std::vector<VehiclePropConfig> VehiclePropertyStore::getAllConfigs() const {
- std::lock_guard<std::mutex> g(mLock);
+ std::scoped_lock<std::mutex> g(mLock);
std::vector<VehiclePropConfig> configs;
configs.reserve(mRecordsByPropId.size());
@@ -226,16 +245,23 @@
}
Result<const VehiclePropConfig*> VehiclePropertyStore::getConfig(int32_t propId) const {
- std::lock_guard<std::mutex> g(mLock);
+ std::scoped_lock<std::mutex> g(mLock);
const VehiclePropertyStore::Record* record = getRecordLocked(propId);
if (record == nullptr) {
- return Errorf("property: {:d} not registered", propId);
+ return Error(toInt(StatusCode::INVALID_ARG)) << "property: " << propId << " not registered";
}
return &record->propConfig;
}
+void VehiclePropertyStore::setOnValueChangeCallback(
+ const VehiclePropertyStore::OnValueChangeCallback& callback) {
+ std::scoped_lock<std::mutex> g(mLock);
+
+ mOnValueChangeCallback = callback;
+}
+
} // namespace vehicle
} // namespace automotive
} // namespace hardware
diff --git a/automotive/vehicle/aidl/impl/utils/common/test/VehiclePropertyStoreTest.cpp b/automotive/vehicle/aidl/impl/utils/common/test/VehiclePropertyStoreTest.cpp
index f1d218d..1f230e7 100644
--- a/automotive/vehicle/aidl/impl/utils/common/test/VehiclePropertyStoreTest.cpp
+++ b/automotive/vehicle/aidl/impl/utils/common/test/VehiclePropertyStoreTest.cpp
@@ -15,6 +15,7 @@
*/
#include <PropertyUtils.h>
+#include <VehicleHalTypes.h>
#include <VehiclePropertyStore.h>
#include <VehicleUtils.h>
#include <gmock/gmock.h>
@@ -27,6 +28,7 @@
namespace {
+using ::aidl::android::hardware::automotive::vehicle::StatusCode;
using ::aidl::android::hardware::automotive::vehicle::VehicleAreaConfig;
using ::aidl::android::hardware::automotive::vehicle::VehiclePropConfig;
using ::aidl::android::hardware::automotive::vehicle::VehicleProperty;
@@ -111,7 +113,8 @@
TEST_F(VehiclePropertyStoreTest, testGetConfigWithInvalidPropId) {
Result<const VehiclePropConfig*> result = mStore->getConfig(INVALID_PROP_ID);
- ASSERT_FALSE(result.ok()) << "expect error when getting a config for an invalid property ID";
+ EXPECT_FALSE(result.ok()) << "expect error when getting a config for an invalid property ID";
+ EXPECT_EQ(result.error().code(), toInt(StatusCode::INVALID_ARG));
}
std::vector<VehiclePropValue> getTestPropValues() {
@@ -180,7 +183,8 @@
TEST_F(VehiclePropertyStoreTest, testReadValuesForPropertyError) {
auto result = mStore->readValuesForProperty(INVALID_PROP_ID);
- ASSERT_FALSE(result.ok()) << "expect error when reading values for an invalid property";
+ EXPECT_FALSE(result.ok()) << "expect error when reading values for an invalid property";
+ EXPECT_EQ(result.error().code(), toInt(StatusCode::INVALID_ARG));
}
TEST_F(VehiclePropertyStoreTest, testReadValueOk) {
@@ -219,15 +223,19 @@
auto result = mStore->readValue(toInt(VehicleProperty::TIRE_PRESSURE), WHEEL_REAR_LEFT);
- ASSERT_FALSE(result.ok()) << "expect error when reading a value that has not been written";
+ EXPECT_FALSE(result.ok()) << "expect error when reading a value that has not been written";
+ EXPECT_EQ(result.error().code(), toInt(StatusCode::NOT_AVAILABLE));
}
TEST_F(VehiclePropertyStoreTest, testWriteValueError) {
auto v = mValuePool->obtain(VehiclePropertyType::FLOAT);
v->prop = INVALID_PROP_ID;
v->value.floatValues = {1.0};
- ASSERT_FALSE(mStore->writeValue(std::move(v)).ok())
- << "expect error when writing value for an invalid property ID";
+
+ auto result = mStore->writeValue(std::move(v));
+
+ EXPECT_FALSE(result.ok()) << "expect error when writing value for an invalid property ID";
+ EXPECT_EQ(result.error().code(), toInt(StatusCode::INVALID_ARG));
}
TEST_F(VehiclePropertyStoreTest, testWriteValueNoAreaConfig) {
@@ -236,8 +244,11 @@
v->value.floatValues = {1.0};
// There is no config for ALL_WHEELS.
v->areaId = ALL_WHEELS;
- ASSERT_FALSE(mStore->writeValue(std::move(v)).ok())
- << "expect error when writing value for an area without config";
+
+ auto result = mStore->writeValue(std::move(v));
+
+ EXPECT_FALSE(result.ok()) << "expect error when writing value for an area without config";
+ EXPECT_EQ(result.error().code(), toInt(StatusCode::INVALID_ARG));
}
TEST_F(VehiclePropertyStoreTest, testWriteOutdatedValue) {
@@ -254,8 +265,11 @@
v2->prop = toInt(VehicleProperty::TIRE_PRESSURE);
v2->value.floatValues = {180.0};
v2->areaId = WHEEL_FRONT_LEFT;
- ASSERT_FALSE(mStore->writeValue(std::move(v2)).ok())
- << "expect error when writing an outdated value";
+
+ auto result = mStore->writeValue(std::move(v2));
+
+ EXPECT_FALSE(result.ok()) << "expect error when writing an outdated value";
+ EXPECT_EQ(result.error().code(), toInt(StatusCode::INVALID_ARG));
}
TEST_F(VehiclePropertyStoreTest, testToken) {
@@ -300,8 +314,10 @@
}
mStore->removeValue(values[0]);
+ auto result = mStore->readValue(values[0]);
- ASSERT_FALSE(mStore->readValue(values[0]).ok()) << "expect error when reading a removed value";
+ EXPECT_FALSE(result.ok()) << "expect error when reading a removed value";
+ EXPECT_EQ(result.error().code(), toInt(StatusCode::NOT_AVAILABLE));
auto leftTirePressureResult = mStore->readValue(values[1]);
@@ -382,6 +398,57 @@
ASSERT_EQ(result.value()->status, VehiclePropertyStatus::AVAILABLE);
}
+TEST_F(VehiclePropertyStoreTest, testPropertyChangeCallbackNewValue) {
+ VehiclePropValue updatedValue;
+ mStore->setOnValueChangeCallback(
+ [&updatedValue](const VehiclePropValue& value) { updatedValue = value; });
+ VehiclePropValue fuelCapacity = {
+ .prop = toInt(VehicleProperty::INFO_FUEL_CAPACITY),
+ .value = {.floatValues = {1.0}},
+ };
+ ASSERT_RESULT_OK(mStore->writeValue(mValuePool->obtain(fuelCapacity)));
+
+ ASSERT_EQ(updatedValue, fuelCapacity);
+}
+
+TEST_F(VehiclePropertyStoreTest, testPropertyChangeCallbackUpdateValue) {
+ VehiclePropValue updatedValue;
+ VehiclePropValue fuelCapacity = {
+ .prop = toInt(VehicleProperty::INFO_FUEL_CAPACITY),
+ .value = {.floatValues = {1.0}},
+ };
+ ASSERT_RESULT_OK(mStore->writeValue(mValuePool->obtain(fuelCapacity)));
+
+ mStore->setOnValueChangeCallback(
+ [&updatedValue](const VehiclePropValue& value) { updatedValue = value; });
+
+ fuelCapacity.value.floatValues[0] = 2.0;
+ fuelCapacity.timestamp = 1;
+
+ ASSERT_RESULT_OK(mStore->writeValue(mValuePool->obtain(fuelCapacity)));
+
+ ASSERT_EQ(updatedValue, fuelCapacity);
+}
+
+TEST_F(VehiclePropertyStoreTest, testPropertyChangeCallbackNoUpdate) {
+ VehiclePropValue updatedValue{
+ .prop = INVALID_PROP_ID,
+ };
+ VehiclePropValue fuelCapacity = {
+ .prop = toInt(VehicleProperty::INFO_FUEL_CAPACITY),
+ .value = {.floatValues = {1.0}},
+ };
+ ASSERT_RESULT_OK(mStore->writeValue(mValuePool->obtain(fuelCapacity)));
+
+ mStore->setOnValueChangeCallback(
+ [&updatedValue](const VehiclePropValue& value) { updatedValue = value; });
+
+ // Write the same value again should succeed but should not trigger callback.
+ ASSERT_RESULT_OK(mStore->writeValue(mValuePool->obtain(fuelCapacity)));
+
+ ASSERT_EQ(updatedValue.prop, INVALID_PROP_ID);
+}
+
} // namespace vehicle
} // namespace automotive
} // namespace hardware
diff --git a/automotive/vehicle/aidl/impl/utils/common/test/VehicleUtilsTest.cpp b/automotive/vehicle/aidl/impl/utils/common/test/VehicleUtilsTest.cpp
index 7ad3d31..131eb3b 100644
--- a/automotive/vehicle/aidl/impl/utils/common/test/VehicleUtilsTest.cpp
+++ b/automotive/vehicle/aidl/impl/utils/common/test/VehicleUtilsTest.cpp
@@ -14,10 +14,14 @@
* limitations under the License.
*/
+#include <ConcurrentQueue.h>
#include <PropertyUtils.h>
#include <VehicleUtils.h>
#include <gtest/gtest.h>
+
+#include <atomic>
+#include <thread>
#include <vector>
namespace android {
@@ -231,7 +235,7 @@
<< "vector size should always be 1 for single value type";
}
-TEST(VehicleUtilsTest, testCreateVehiclePropValueFloVecatVec) {
+TEST(VehicleUtilsTest, testCreateVehiclePropValueFloatVecMultiValues) {
std::unique_ptr<VehiclePropValue> value =
createVehiclePropValueVec(VehiclePropertyType::FLOAT_VEC, /*vecSize=*/2);
@@ -247,6 +251,90 @@
ASSERT_EQ(2u, value->value.byteValues.size());
}
+TEST(VehicleUtilsTest, testConcurrentQueueOneThread) {
+ ConcurrentQueue<int> queue;
+
+ queue.push(1);
+ queue.push(2);
+ auto result = queue.flush();
+
+ ASSERT_EQ(result, std::vector<int>({1, 2}));
+}
+
+TEST(VehicleUtilsTest, testConcurrentQueueMultipleThreads) {
+ ConcurrentQueue<int> queue;
+ std::vector<int> results;
+ std::atomic<bool> stop = false;
+
+ std::thread t1([&queue]() {
+ for (int i = 0; i < 100; i++) {
+ queue.push(0);
+ }
+ });
+ std::thread t2([&queue]() {
+ for (int i = 0; i < 100; i++) {
+ queue.push(1);
+ }
+ });
+ std::thread t3([&queue, &results, &stop]() {
+ while (!stop) {
+ queue.waitForItems();
+ for (int i : queue.flush()) {
+ results.push_back(i);
+ }
+ }
+
+ // After we stop, get all the remaining values in the queue.
+ for (int i : queue.flush()) {
+ results.push_back(i);
+ }
+ });
+
+ t1.join();
+ t2.join();
+
+ stop = true;
+ queue.deactivate();
+ t3.join();
+
+ size_t zeroCount = 0;
+ size_t oneCount = 0;
+ for (int i : results) {
+ if (i == 0) {
+ zeroCount++;
+ }
+ if (i == 1) {
+ oneCount++;
+ }
+ }
+
+ EXPECT_EQ(results.size(), static_cast<size_t>(200));
+ EXPECT_EQ(zeroCount, static_cast<size_t>(100));
+ EXPECT_EQ(oneCount, static_cast<size_t>(100));
+}
+
+TEST(VehicleUtilsTest, testConcurrentQueuePushAfterDeactivate) {
+ ConcurrentQueue<int> queue;
+
+ queue.deactivate();
+ queue.push(1);
+
+ ASSERT_TRUE(queue.flush().empty());
+}
+
+TEST(VehicleUtilsTest, testConcurrentQueueDeactivateNotifyWaitingThread) {
+ ConcurrentQueue<int> queue;
+
+ std::thread t([&queue]() {
+ // This would block until queue is deactivated.
+ queue.waitForItems();
+ });
+
+ queue.deactivate();
+
+ t.join();
+}
+
} // namespace vehicle
} // namespace automotive
} // namespace hardware
diff --git a/graphics/composer/2.2/vts/functional/OWNERS b/graphics/composer/2.2/vts/functional/OWNERS
index ea06752..31b0dc7 100644
--- a/graphics/composer/2.2/vts/functional/OWNERS
+++ b/graphics/composer/2.2/vts/functional/OWNERS
@@ -1,7 +1,5 @@
+# Bug component: 25423
# Graphics team
adyabr@google.com
lpy@google.com
-
-# VTS team
-yim@google.com
-zhuoyao@google.com
+sumir@google.com
diff --git a/graphics/composer/2.3/vts/functional/OWNERS b/graphics/composer/2.3/vts/functional/OWNERS
index ea06752..31b0dc7 100644
--- a/graphics/composer/2.3/vts/functional/OWNERS
+++ b/graphics/composer/2.3/vts/functional/OWNERS
@@ -1,7 +1,5 @@
+# Bug component: 25423
# Graphics team
adyabr@google.com
lpy@google.com
-
-# VTS team
-yim@google.com
-zhuoyao@google.com
+sumir@google.com
diff --git a/graphics/composer/2.4/vts/functional/OWNERS b/graphics/composer/2.4/vts/functional/OWNERS
index ea06752..31b0dc7 100644
--- a/graphics/composer/2.4/vts/functional/OWNERS
+++ b/graphics/composer/2.4/vts/functional/OWNERS
@@ -1,7 +1,5 @@
+# Bug component: 25423
# Graphics team
adyabr@google.com
lpy@google.com
-
-# VTS team
-yim@google.com
-zhuoyao@google.com
+sumir@google.com
diff --git a/graphics/composer/aidl/Android.bp b/graphics/composer/aidl/Android.bp
index 5006185..e33c653 100644
--- a/graphics/composer/aidl/Android.bp
+++ b/graphics/composer/aidl/Android.bp
@@ -67,3 +67,22 @@
],
export_include_dirs: ["include"],
}
+
+cc_library_headers {
+ name: "android.hardware.graphics.composer3-command-buffer",
+ vendor_available: true,
+ shared_libs: [
+ "android.hardware.graphics.composer3-V1-ndk",
+ "libbase",
+ "libfmq",
+ "libsync",
+ ],
+ static_libs: [
+ "libaidlcommonsupport",
+ ],
+ export_shared_lib_headers: [
+ "libfmq",
+ "libsync",
+ ],
+ export_include_dirs: ["include"],
+}
diff --git a/graphics/composer/aidl/OWNERS b/graphics/composer/aidl/OWNERS
new file mode 100644
index 0000000..9028d9d
--- /dev/null
+++ b/graphics/composer/aidl/OWNERS
@@ -0,0 +1,6 @@
+# Bug component: 1075131
+
+# Graphics team
+adyabr@google.com
+alecmouri@google.com
+sumir@google.com
\ No newline at end of file
diff --git a/graphics/composer/aidl/aidl_api/android.hardware.graphics.composer3/current/android/hardware/graphics/composer3/Capability.aidl b/graphics/composer/aidl/aidl_api/android.hardware.graphics.composer3/current/android/hardware/graphics/composer3/Capability.aidl
index 557b3f8..9c49583 100644
--- a/graphics/composer/aidl/aidl_api/android.hardware.graphics.composer3/current/android/hardware/graphics/composer3/Capability.aidl
+++ b/graphics/composer/aidl/aidl_api/android.hardware.graphics.composer3/current/android/hardware/graphics/composer3/Capability.aidl
@@ -38,4 +38,5 @@
SIDEBAND_STREAM = 1,
SKIP_CLIENT_COLOR_TRANSFORM = 2,
PRESENT_FENCE_IS_NOT_RELIABLE = 3,
+ SKIP_VALIDATE = 4,
}
diff --git a/graphics/composer/aidl/aidl_api/android.hardware.graphics.composer3/current/android/hardware/graphics/composer3/IComposerClient.aidl b/graphics/composer/aidl/aidl_api/android.hardware.graphics.composer3/current/android/hardware/graphics/composer3/IComposerClient.aidl
index 8824f5a..5c37018 100644
--- a/graphics/composer/aidl/aidl_api/android.hardware.graphics.composer3/current/android/hardware/graphics/composer3/IComposerClient.aidl
+++ b/graphics/composer/aidl/aidl_api/android.hardware.graphics.composer3/current/android/hardware/graphics/composer3/IComposerClient.aidl
@@ -40,7 +40,6 @@
void destroyVirtualDisplay(long display);
android.hardware.graphics.composer3.ExecuteCommandsStatus executeCommands(int inLength, in android.hardware.common.NativeHandle[] inHandles);
int getActiveConfig(long display);
- void getClientTargetSupport(long display, int width, int height, in android.hardware.graphics.composer3.ClientTargetProperty clientTargetProperty);
android.hardware.graphics.composer3.ColorMode[] getColorModes(long display);
float[] getDataspaceSaturationMatrix(android.hardware.graphics.common.Dataspace dataspace);
int getDisplayAttribute(long display, int config, android.hardware.graphics.composer3.DisplayAttribute attribute);
diff --git a/graphics/composer/aidl/android/hardware/graphics/composer3/Capability.aidl b/graphics/composer/aidl/android/hardware/graphics/composer3/Capability.aidl
index 028b6f5..ea619ae 100644
--- a/graphics/composer/aidl/android/hardware/graphics/composer3/Capability.aidl
+++ b/graphics/composer/aidl/android/hardware/graphics/composer3/Capability.aidl
@@ -46,4 +46,15 @@
* representation of the actual present time of a frame.
*/
PRESENT_FENCE_IS_NOT_RELIABLE = 3,
+ /**
+ * Specifies that a device is able to skip the validateDisplay call before
+ * receiving a call to presentDisplay. The client will always skip
+ * validateDisplay and try to call presentDisplay regardless of the changes
+ * in the properties of the layers. If the device returns anything else than
+ * no error, it will call validateDisplay then presentDisplay again.
+ * For this capability to be worthwhile the device implementation of
+ * presentDisplay should fail as fast as possible in the case a
+ * validateDisplay step is needed.
+ */
+ SKIP_VALIDATE = 4,
}
diff --git a/graphics/composer/aidl/android/hardware/graphics/composer3/IComposerClient.aidl b/graphics/composer/aidl/android/hardware/graphics/composer3/IComposerClient.aidl
index 20a6ffc..2a9545d 100644
--- a/graphics/composer/aidl/android/hardware/graphics/composer3/IComposerClient.aidl
+++ b/graphics/composer/aidl/android/hardware/graphics/composer3/IComposerClient.aidl
@@ -189,25 +189,6 @@
int getActiveConfig(long display);
/**
- * Returns whether a client target with the given properties can be
- * handled by the device.
- *
- * This function must return true for a client target with width and
- * height equal to the active display configuration dimensions,
- * PixelFormat::RGBA_8888, and Dataspace::UNKNOWN. It is not required to
- * return true for any other configuration.
- *
- * @param display is the display to query.
- * @param width is the client target width in pixels.
- * @param height is the client target height in pixels.
- * @param clientTargetProperty is the client target format and dataspace.
- * @exception EX_BAD_DISPLAY when an invalid display handle was passed in.
- * @exception EX_UNSUPPORTED when the given configuration is not supported.
- */
- void getClientTargetSupport(
- long display, int width, int height, in ClientTargetProperty clientTargetProperty);
-
- /**
* Returns the color modes supported on this display.
*
* All devices must support at least ColorMode::NATIVE.
diff --git a/graphics/composer/aidl/android/hardware/graphics/composer3/translate-ndk.cpp b/graphics/composer/aidl/android/hardware/graphics/composer3/translate-ndk.cpp
index a593e90..d59190d 100644
--- a/graphics/composer/aidl/android/hardware/graphics/composer3/translate-ndk.cpp
+++ b/graphics/composer/aidl/android/hardware/graphics/composer3/translate-ndk.cpp
@@ -70,6 +70,9 @@
static_cast<aidl::android::hardware::graphics::composer3::Capability>(
::android::hardware::graphics::composer::V2_1::IComposer::Capability::
PRESENT_FENCE_IS_NOT_RELIABLE));
+// HWC2_CAPABILITY_SKIP_VALIDATE was never defined for HIDL, so we just hardcode its value
+static_assert(aidl::android::hardware::graphics::composer3::Capability::SKIP_VALIDATE ==
+ static_cast<aidl::android::hardware::graphics::composer3::Capability>(4));
static_assert(aidl::android::hardware::graphics::composer3::LayerRequest::CLEAR_CLIENT_TARGET ==
static_cast<aidl::android::hardware::graphics::composer3::LayerRequest>(
diff --git a/graphics/composer/aidl/include/android/hardware/graphics/composer3/command-buffer.h b/graphics/composer/aidl/include/android/hardware/graphics/composer3/command-buffer.h
new file mode 100644
index 0000000..d02cf9c
--- /dev/null
+++ b/graphics/composer/aidl/include/android/hardware/graphics/composer3/command-buffer.h
@@ -0,0 +1,895 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <algorithm>
+#include <limits>
+#include <memory>
+#include <vector>
+
+#include <inttypes.h>
+#include <string.h>
+
+#include <aidl/android/hardware/graphics/composer3/BlendMode.h>
+#include <aidl/android/hardware/graphics/composer3/ClientTargetProperty.h>
+#include <aidl/android/hardware/graphics/composer3/Color.h>
+#include <aidl/android/hardware/graphics/composer3/Command.h>
+#include <aidl/android/hardware/graphics/composer3/Composition.h>
+#include <aidl/android/hardware/graphics/composer3/FloatColor.h>
+#include <aidl/android/hardware/graphics/composer3/HandleIndex.h>
+#include <aidl/android/hardware/graphics/composer3/IComposer.h>
+#include <aidl/android/hardware/graphics/composer3/IComposerClient.h>
+#include <aidl/android/hardware/graphics/composer3/PerFrameMetadata.h>
+#include <aidl/android/hardware/graphics/composer3/PerFrameMetadataBlob.h>
+
+#include <aidl/android/hardware/graphics/common/ColorTransform.h>
+#include <aidl/android/hardware/graphics/common/FRect.h>
+#include <aidl/android/hardware/graphics/common/Rect.h>
+#include <aidl/android/hardware/graphics/common/Transform.h>
+
+#include <fmq/AidlMessageQueue.h>
+#include <log/log.h>
+#include <sync/sync.h>
+
+#include <aidlcommonsupport/NativeHandle.h>
+
+using aidl::android::hardware::graphics::common::ColorTransform;
+using aidl::android::hardware::graphics::common::Dataspace;
+using aidl::android::hardware::graphics::common::FRect;
+using aidl::android::hardware::graphics::common::Rect;
+using aidl::android::hardware::graphics::common::Transform;
+
+using aidl::android::hardware::graphics::composer3::BlendMode;
+using aidl::android::hardware::graphics::composer3::ClientTargetProperty;
+using aidl::android::hardware::graphics::composer3::Color;
+using aidl::android::hardware::graphics::composer3::Command;
+using aidl::android::hardware::graphics::composer3::Composition;
+using aidl::android::hardware::graphics::composer3::FloatColor;
+using aidl::android::hardware::graphics::composer3::HandleIndex;
+using aidl::android::hardware::graphics::composer3::PerFrameMetadata;
+using aidl::android::hardware::graphics::composer3::PerFrameMetadataBlob;
+
+using aidl::android::hardware::common::NativeHandle;
+using aidl::android::hardware::common::fmq::SynchronizedReadWrite;
+using android::AidlMessageQueue;
+using CommandQueueType = AidlMessageQueue<int32_t, SynchronizedReadWrite>;
+using aidl::android::hardware::common::fmq::MQDescriptor;
+using DescriptorType = MQDescriptor<int32_t, SynchronizedReadWrite>;
+
+namespace aidl::android::hardware::graphics::composer3 {
+
+// This class helps build a command queue. Note that all sizes/lengths are in
+// units of uint32_t's.
+class CommandWriterBase {
+ public:
+ CommandWriterBase(uint32_t initialMaxSize) : mDataMaxSize(initialMaxSize) {
+ mData = std::make_unique<int32_t[]>(mDataMaxSize);
+ reset();
+ }
+
+ virtual ~CommandWriterBase() { reset(); }
+
+ void reset() {
+ mDataWritten = 0;
+ mCommandEnd = 0;
+
+ // handles in mDataHandles are owned by the caller
+ mDataHandles.clear();
+
+ // handles in mTemporaryHandles are owned by the writer
+ for (auto handle : mTemporaryHandles) {
+ native_handle_close(handle);
+ native_handle_delete(handle);
+ }
+ mTemporaryHandles.clear();
+ }
+
+ Command getCommand(uint32_t offset) {
+ uint32_t val = (offset < mDataWritten) ? mData[offset] : 0;
+ return static_cast<Command>(val & static_cast<uint32_t>(Command::OPCODE_MASK));
+ }
+
+ bool writeQueue(bool* outQueueChanged, int32_t* outCommandLength,
+ std::vector<NativeHandle>* outCommandHandles) {
+ if (mDataWritten == 0) {
+ *outQueueChanged = false;
+ *outCommandLength = 0;
+ outCommandHandles->clear();
+ return true;
+ }
+
+ // After data are written to the queue, it may not be read by the
+ // remote reader when
+ //
+ // - the writer does not send them (because of other errors)
+ // - the hwbinder transaction fails
+ // - the reader does not read them (because of other errors)
+ //
+ // Discard the stale data here.
+ size_t staleDataSize = mQueue ? mQueue->availableToRead() : 0;
+ if (staleDataSize > 0) {
+ ALOGW("discarding stale data from message queue");
+ CommandQueueType::MemTransaction tx;
+ if (mQueue->beginRead(staleDataSize, &tx)) {
+ mQueue->commitRead(staleDataSize);
+ }
+ }
+
+ // write data to queue, optionally resizing it
+ if (mQueue && (mDataMaxSize <= mQueue->getQuantumCount())) {
+ if (!mQueue->write(mData.get(), mDataWritten)) {
+ ALOGE("failed to write commands to message queue");
+ return false;
+ }
+
+ *outQueueChanged = false;
+ } else {
+ auto newQueue = std::make_unique<CommandQueueType>(mDataMaxSize);
+ if (!newQueue->isValid() || !newQueue->write(mData.get(), mDataWritten)) {
+ ALOGE("failed to prepare a new message queue ");
+ return false;
+ }
+
+ mQueue = std::move(newQueue);
+ *outQueueChanged = true;
+ }
+
+ *outCommandLength = mDataWritten;
+ *outCommandHandles = std::move(mDataHandles);
+
+ return true;
+ }
+
+ DescriptorType getMQDescriptor() const {
+ return (mQueue) ? mQueue->dupeDesc() : DescriptorType{};
+ }
+
+ static constexpr uint16_t kSelectDisplayLength = 2;
+ void selectDisplay(int64_t display) {
+ beginCommand(Command::SELECT_DISPLAY, kSelectDisplayLength);
+ write64(display);
+ endCommand();
+ }
+
+ static constexpr uint16_t kSelectLayerLength = 2;
+ void selectLayer(int64_t layer) {
+ beginCommand(Command::SELECT_LAYER, kSelectLayerLength);
+ write64(layer);
+ endCommand();
+ }
+
+ static constexpr uint16_t kSetErrorLength = 2;
+ void setError(uint32_t location, int32_t error) {
+ beginCommand(Command::SET_ERROR, kSetErrorLength);
+ write(location);
+ writeSigned(error);
+ endCommand();
+ }
+
+ static constexpr uint32_t kPresentOrValidateDisplayResultLength = 1;
+ void setPresentOrValidateResult(uint32_t state) {
+ beginCommand(Command::SET_PRESENT_OR_VALIDATE_DISPLAY_RESULT,
+ kPresentOrValidateDisplayResultLength);
+ write(state);
+ endCommand();
+ }
+
+ void setChangedCompositionTypes(const std::vector<int64_t>& layers,
+ const std::vector<Composition>& types) {
+ size_t totalLayers = std::min(layers.size(), types.size());
+ size_t currentLayer = 0;
+
+ while (currentLayer < totalLayers) {
+ size_t count =
+ std::min(totalLayers - currentLayer, static_cast<size_t>(kMaxLength) / 3);
+
+ beginCommand(Command::SET_CHANGED_COMPOSITION_TYPES, count * 3);
+ for (size_t i = 0; i < count; i++) {
+ write64(layers[currentLayer + i]);
+ writeSigned(static_cast<int32_t>(types[currentLayer + i]));
+ }
+ endCommand();
+
+ currentLayer += count;
+ }
+ }
+
+ void setDisplayRequests(uint32_t displayRequestMask, const std::vector<int64_t>& layers,
+ const std::vector<uint32_t>& layerRequestMasks) {
+ size_t totalLayers = std::min(layers.size(), layerRequestMasks.size());
+ size_t currentLayer = 0;
+
+ while (currentLayer < totalLayers) {
+ size_t count =
+ std::min(totalLayers - currentLayer, static_cast<size_t>(kMaxLength - 1) / 3);
+
+ beginCommand(Command::SET_DISPLAY_REQUESTS, 1 + count * 3);
+ write(displayRequestMask);
+ for (size_t i = 0; i < count; i++) {
+ write64(layers[currentLayer + i]);
+ write(static_cast<int32_t>(layerRequestMasks[currentLayer + i]));
+ }
+ endCommand();
+
+ currentLayer += count;
+ }
+ }
+
+ static constexpr uint16_t kSetPresentFenceLength = 1;
+ void setPresentFence(int presentFence) {
+ beginCommand(Command::SET_PRESENT_FENCE, kSetPresentFenceLength);
+ writeFence(presentFence);
+ endCommand();
+ }
+
+ void setReleaseFences(const std::vector<int64_t>& layers,
+ const std::vector<int>& releaseFences) {
+ size_t totalLayers = std::min(layers.size(), releaseFences.size());
+ size_t currentLayer = 0;
+
+ while (currentLayer < totalLayers) {
+ size_t count =
+ std::min(totalLayers - currentLayer, static_cast<size_t>(kMaxLength) / 3);
+
+ beginCommand(Command::SET_RELEASE_FENCES, count * 3);
+ for (size_t i = 0; i < count; i++) {
+ write64(layers[currentLayer + i]);
+ writeFence(releaseFences[currentLayer + i]);
+ }
+ endCommand();
+
+ currentLayer += count;
+ }
+ }
+
+ static constexpr uint16_t kSetColorTransformLength = 17;
+ void setColorTransform(const float* matrix, ColorTransform hint) {
+ beginCommand(Command::SET_COLOR_TRANSFORM, kSetColorTransformLength);
+ for (int i = 0; i < 16; i++) {
+ writeFloat(matrix[i]);
+ }
+ writeSigned(static_cast<int32_t>(hint));
+ endCommand();
+ }
+
+ void setClientTarget(uint32_t slot, const native_handle_t* target, int acquireFence,
+ Dataspace dataspace, const std::vector<Rect>& damage) {
+ setClientTargetInternal(slot, target, acquireFence, static_cast<int32_t>(dataspace),
+ damage);
+ }
+
+ static constexpr uint16_t kSetOutputBufferLength = 3;
+ void setOutputBuffer(uint32_t slot, const native_handle_t* buffer, int releaseFence) {
+ beginCommand(Command::SET_OUTPUT_BUFFER, kSetOutputBufferLength);
+ write(slot);
+ writeHandle(buffer, true);
+ writeFence(releaseFence);
+ endCommand();
+ }
+
+ static constexpr uint16_t kValidateDisplayLength = 0;
+ void validateDisplay() {
+ beginCommand(Command::VALIDATE_DISPLAY, kValidateDisplayLength);
+ endCommand();
+ }
+
+ static constexpr uint16_t kPresentOrValidateDisplayLength = 0;
+ void presentOrvalidateDisplay() {
+ beginCommand(Command::PRESENT_OR_VALIDATE_DISPLAY, kPresentOrValidateDisplayLength);
+ endCommand();
+ }
+
+ static constexpr uint16_t kAcceptDisplayChangesLength = 0;
+ void acceptDisplayChanges() {
+ beginCommand(Command::ACCEPT_DISPLAY_CHANGES, kAcceptDisplayChangesLength);
+ endCommand();
+ }
+
+ static constexpr uint16_t kPresentDisplayLength = 0;
+ void presentDisplay() {
+ beginCommand(Command::PRESENT_DISPLAY, kPresentDisplayLength);
+ endCommand();
+ }
+
+ static constexpr uint16_t kSetLayerCursorPositionLength = 2;
+ void setLayerCursorPosition(int32_t x, int32_t y) {
+ beginCommand(Command::SET_LAYER_CURSOR_POSITION, kSetLayerCursorPositionLength);
+ writeSigned(x);
+ writeSigned(y);
+ endCommand();
+ }
+
+ static constexpr uint16_t kSetLayerBufferLength = 3;
+ void setLayerBuffer(uint32_t slot, const native_handle_t* buffer, int acquireFence) {
+ beginCommand(Command::SET_LAYER_BUFFER, kSetLayerBufferLength);
+ write(slot);
+ writeHandle(buffer, true);
+ writeFence(acquireFence);
+ endCommand();
+ }
+
+ void setLayerSurfaceDamage(const std::vector<Rect>& damage) {
+ bool doWrite = (damage.size() <= kMaxLength / 4);
+ size_t length = (doWrite) ? damage.size() * 4 : 0;
+
+ beginCommand(Command::SET_LAYER_SURFACE_DAMAGE, length);
+ // When there are too many rectangles in the damage region and doWrite
+ // is false, we write no rectangle at all which means the entire
+ // layer is damaged.
+ if (doWrite) {
+ writeRegion(damage);
+ }
+ endCommand();
+ }
+
+ static constexpr uint16_t kSetLayerBlendModeLength = 1;
+ void setLayerBlendMode(BlendMode mode) {
+ beginCommand(Command::SET_LAYER_BLEND_MODE, kSetLayerBlendModeLength);
+ writeSigned(static_cast<int32_t>(mode));
+ endCommand();
+ }
+
+ static constexpr uint16_t kSetLayerColorLength = 1;
+ void setLayerColor(Color color) {
+ beginCommand(Command::SET_LAYER_COLOR, kSetLayerColorLength);
+ writeColor(color);
+ endCommand();
+ }
+
+ static constexpr uint16_t kSetLayerCompositionTypeLength = 1;
+ void setLayerCompositionType(Composition type) {
+ beginCommand(Command::SET_LAYER_COMPOSITION_TYPE, kSetLayerCompositionTypeLength);
+ writeSigned(static_cast<int32_t>(type));
+ endCommand();
+ }
+
+ static constexpr uint16_t kSetLayerDataspaceLength = 1;
+ void setLayerDataspace(Dataspace dataspace) {
+ setLayerDataspaceInternal(static_cast<int32_t>(dataspace));
+ }
+
+ static constexpr uint16_t kSetLayerDisplayFrameLength = 4;
+ void setLayerDisplayFrame(const Rect& frame) {
+ beginCommand(Command::SET_LAYER_DISPLAY_FRAME, kSetLayerDisplayFrameLength);
+ writeRect(frame);
+ endCommand();
+ }
+
+ static constexpr uint16_t kSetLayerPlaneAlphaLength = 1;
+ void setLayerPlaneAlpha(float alpha) {
+ beginCommand(Command::SET_LAYER_PLANE_ALPHA, kSetLayerPlaneAlphaLength);
+ writeFloat(alpha);
+ endCommand();
+ }
+
+ static constexpr uint16_t kSetLayerSidebandStreamLength = 1;
+ void setLayerSidebandStream(const native_handle_t* stream) {
+ beginCommand(Command::SET_LAYER_SIDEBAND_STREAM, kSetLayerSidebandStreamLength);
+ writeHandle(stream);
+ endCommand();
+ }
+
+ static constexpr uint16_t kSetLayerSourceCropLength = 4;
+ void setLayerSourceCrop(const FRect& crop) {
+ beginCommand(Command::SET_LAYER_SOURCE_CROP, kSetLayerSourceCropLength);
+ writeFRect(crop);
+ endCommand();
+ }
+
+ static constexpr uint16_t kSetLayerTransformLength = 1;
+ void setLayerTransform(Transform transform) {
+ beginCommand(Command::SET_LAYER_TRANSFORM, kSetLayerTransformLength);
+ writeSigned(static_cast<int32_t>(transform));
+ endCommand();
+ }
+
+ void setLayerVisibleRegion(const std::vector<Rect>& visible) {
+ bool doWrite = (visible.size() <= kMaxLength / 4);
+ size_t length = (doWrite) ? visible.size() * 4 : 0;
+
+ beginCommand(Command::SET_LAYER_VISIBLE_REGION, length);
+ // When there are too many rectangles in the visible region and
+ // doWrite is false, we write no rectangle at all which means the
+ // entire layer is visible.
+ if (doWrite) {
+ writeRegion(visible);
+ }
+ endCommand();
+ }
+
+ static constexpr uint16_t kSetLayerZOrderLength = 1;
+ void setLayerZOrder(uint32_t z) {
+ beginCommand(Command::SET_LAYER_Z_ORDER, kSetLayerZOrderLength);
+ write(z);
+ endCommand();
+ }
+
+ void setLayerPerFrameMetadata(const std::vector<PerFrameMetadata>& metadataVec) {
+ beginCommand(Command::SET_LAYER_PER_FRAME_METADATA, metadataVec.size() * 2);
+ for (const auto& metadata : metadataVec) {
+ writeSigned(static_cast<int32_t>(metadata.key));
+ writeFloat(metadata.value);
+ }
+ endCommand();
+ }
+
+ static constexpr uint16_t kSetLayerColorTransformLength = 16;
+ void setLayerColorTransform(const float* matrix) {
+ beginCommand(Command::SET_LAYER_COLOR_TRANSFORM, kSetLayerColorTransformLength);
+ for (int i = 0; i < 16; i++) {
+ writeFloat(matrix[i]);
+ }
+ endCommand();
+ }
+
+ void setLayerPerFrameMetadataBlobs(const std::vector<PerFrameMetadataBlob>& metadata) {
+ // in units of uint32_t's
+ size_t commandLength = 0;
+
+ if (metadata.size() > std::numeric_limits<uint32_t>::max()) {
+ LOG_FATAL("too many metadata blobs - dynamic metadata size is too large");
+ return;
+ }
+
+ // space for numElements
+ commandLength += 1;
+
+ for (auto metadataBlob : metadata) {
+ commandLength += 1; // key of metadata blob
+ commandLength += 1; // size information of metadata blob
+
+ // metadata content size
+ size_t metadataSize = metadataBlob.blob.size() / sizeof(uint32_t);
+ commandLength += metadataSize;
+ commandLength +=
+ (metadataBlob.blob.size() - (metadataSize * sizeof(uint32_t)) > 0) ? 1 : 0;
+ }
+
+ if (commandLength > std::numeric_limits<uint16_t>::max()) {
+ LOG_FATAL("dynamic metadata size is too large");
+ return;
+ }
+
+ // Blobs are written as:
+ // {numElements, key1, size1, blob1, key2, size2, blob2, key3, size3...}
+ uint16_t length = static_cast<uint16_t>(commandLength);
+ beginCommand(Command::SET_LAYER_PER_FRAME_METADATA_BLOBS, length);
+ write(static_cast<uint32_t>(metadata.size()));
+ for (auto metadataBlob : metadata) {
+ writeSigned(static_cast<int32_t>(metadataBlob.key));
+ write(static_cast<uint32_t>(metadataBlob.blob.size()));
+ writeBlob(static_cast<uint32_t>(metadataBlob.blob.size()), metadataBlob.blob.data());
+ }
+ endCommand();
+ }
+
+ static constexpr uint16_t kSetLayerFloatColorLength = 4;
+ void setLayerFloatColor(FloatColor color) {
+ beginCommand(Command::SET_LAYER_FLOAT_COLOR, kSetLayerFloatColorLength);
+ writeFloatColor(color);
+ endCommand();
+ }
+
+ static constexpr uint16_t kSetClientTargetPropertyLength = 2;
+ void setClientTargetProperty(const ClientTargetProperty& clientTargetProperty) {
+ beginCommand(Command::SET_CLIENT_TARGET_PROPERTY, kSetClientTargetPropertyLength);
+ writeSigned(static_cast<int32_t>(clientTargetProperty.pixelFormat));
+ writeSigned(static_cast<int32_t>(clientTargetProperty.dataspace));
+ endCommand();
+ }
+
+ void setLayerGenericMetadata(const std::string& key, const bool mandatory,
+ const std::vector<uint8_t>& value) {
+ const size_t commandSize = 3 + sizeToElements(key.size()) + sizeToElements(value.size());
+ if (commandSize > std::numeric_limits<uint16_t>::max()) {
+ LOG_FATAL("Too much generic metadata (%zu elements)", commandSize);
+ return;
+ }
+
+ beginCommand(Command::SET_LAYER_GENERIC_METADATA, static_cast<uint16_t>(commandSize));
+ write(key.size());
+ writeBlob(key.size(), reinterpret_cast<const unsigned char*>(key.c_str()));
+ write(mandatory);
+ write(value.size());
+ writeBlob(value.size(), value.data());
+ endCommand();
+ }
+
+ protected:
+ template <typename T>
+ void beginCommand(T command, uint16_t length) {
+ beginCommandBase(static_cast<Command>(command), length);
+ }
+
+ void setClientTargetInternal(uint32_t slot, const native_handle_t* target, int acquireFence,
+ int32_t dataspace, const std::vector<Rect>& damage) {
+ bool doWrite = (damage.size() <= (kMaxLength - 4) / 4);
+ size_t length = 4 + ((doWrite) ? damage.size() * 4 : 0);
+
+ beginCommand(Command::SET_CLIENT_TARGET, length);
+ write(slot);
+ writeHandle(target, true);
+ writeFence(acquireFence);
+ writeSigned(dataspace);
+ // When there are too many rectangles in the damage region and doWrite
+ // is false, we write no rectangle at all which means the entire
+ // client target is damaged.
+ if (doWrite) {
+ writeRegion(damage);
+ }
+ endCommand();
+ }
+
+ void setLayerDataspaceInternal(int32_t dataspace) {
+ beginCommand(Command::SET_LAYER_DATASPACE, kSetLayerDataspaceLength);
+ writeSigned(dataspace);
+ endCommand();
+ }
+
+ void beginCommandBase(Command command, uint16_t length) {
+ if (mCommandEnd) {
+ LOG_FATAL("endCommand was not called before command 0x%x", command);
+ }
+
+ growData(1 + length);
+ write(static_cast<uint32_t>(command) | length);
+
+ mCommandEnd = mDataWritten + length;
+ }
+
+ void endCommand() {
+ if (!mCommandEnd) {
+ LOG_FATAL("beginCommand was not called");
+ } else if (mDataWritten > mCommandEnd) {
+ LOG_FATAL("too much data written");
+ mDataWritten = mCommandEnd;
+ } else if (mDataWritten < mCommandEnd) {
+ LOG_FATAL("too little data written");
+ while (mDataWritten < mCommandEnd) {
+ write(0);
+ }
+ }
+
+ mCommandEnd = 0;
+ }
+
+ void write(uint32_t val) { mData[mDataWritten++] = val; }
+
+ void writeSigned(int32_t val) { memcpy(&mData[mDataWritten++], &val, sizeof(val)); }
+
+ void writeFloat(float val) { memcpy(&mData[mDataWritten++], &val, sizeof(val)); }
+
+ void write64(uint64_t val) {
+ uint32_t lo = static_cast<uint32_t>(val & 0xffffffff);
+ uint32_t hi = static_cast<uint32_t>(val >> 32);
+ write(lo);
+ write(hi);
+ }
+
+ void writeRect(const Rect& rect) {
+ writeSigned(rect.left);
+ writeSigned(rect.top);
+ writeSigned(rect.right);
+ writeSigned(rect.bottom);
+ }
+
+ void writeRegion(const std::vector<Rect>& region) {
+ for (const auto& rect : region) {
+ writeRect(rect);
+ }
+ }
+
+ void writeFRect(const FRect& rect) {
+ writeFloat(rect.left);
+ writeFloat(rect.top);
+ writeFloat(rect.right);
+ writeFloat(rect.bottom);
+ }
+
+ void writeColor(const Color& color) {
+ write((color.r << 0) | (color.g << 8) | (color.b << 16) | (color.a << 24));
+ }
+
+ void writeFloatColor(const FloatColor& color) {
+ writeFloat(color.r);
+ writeFloat(color.g);
+ writeFloat(color.b);
+ writeFloat(color.a);
+ }
+
+ void writeBlob(uint32_t length, const unsigned char* blob) {
+ memcpy(&mData[mDataWritten], blob, length);
+ uint32_t numElements = length / 4;
+ mDataWritten += numElements;
+ mDataWritten += (length - (numElements * 4) > 0) ? 1 : 0;
+ }
+
+ // ownership of handle is not transferred
+ void writeHandle(const native_handle_t* handle, bool useCache) {
+ if (!handle) {
+ writeSigned(
+ static_cast<int32_t>((useCache) ? HandleIndex::CACHED : HandleIndex::EMPTY));
+ return;
+ }
+
+ mDataHandles.push_back(::android::dupToAidl(handle));
+ writeSigned(mDataHandles.size() - 1);
+ }
+
+ void writeHandle(const native_handle_t* handle) { writeHandle(handle, false); }
+
+ // ownership of fence is transferred
+ void writeFence(int fence) {
+ native_handle_t* handle = nullptr;
+ if (fence >= 0) {
+ handle = getTemporaryHandle(1, 0);
+ if (handle) {
+ handle->data[0] = fence;
+ } else {
+ ALOGW("failed to get temporary handle for fence %d", fence);
+ sync_wait(fence, -1);
+ close(fence);
+ }
+ }
+
+ writeHandle(handle);
+ }
+
+ native_handle_t* getTemporaryHandle(int numFds, int numInts) {
+ native_handle_t* handle = native_handle_create(numFds, numInts);
+ if (handle) {
+ mTemporaryHandles.push_back(handle);
+ }
+ return handle;
+ }
+
+ static constexpr uint16_t kMaxLength = std::numeric_limits<uint16_t>::max();
+
+ std::unique_ptr<int32_t[]> mData;
+ uint32_t mDataWritten;
+
+ private:
+ void growData(uint32_t grow) {
+ uint32_t newWritten = mDataWritten + grow;
+ if (newWritten < mDataWritten) {
+ LOG_ALWAYS_FATAL("buffer overflowed; data written %" PRIu32 ", growing by %" PRIu32,
+ mDataWritten, grow);
+ }
+
+ if (newWritten <= mDataMaxSize) {
+ return;
+ }
+
+ uint32_t newMaxSize = mDataMaxSize << 1;
+ if (newMaxSize < newWritten) {
+ newMaxSize = newWritten;
+ }
+
+ auto newData = std::make_unique<int32_t[]>(newMaxSize);
+ std::copy_n(mData.get(), mDataWritten, newData.get());
+ mDataMaxSize = newMaxSize;
+ mData = std::move(newData);
+ }
+
+ uint32_t sizeToElements(uint32_t size) { return (size + 3) / 4; }
+
+ uint32_t mDataMaxSize;
+ // end offset of the current command
+ uint32_t mCommandEnd;
+
+ std::vector<NativeHandle> mDataHandles;
+ std::vector<native_handle_t*> mTemporaryHandles;
+
+ std::unique_ptr<CommandQueueType> mQueue;
+};
+
+// This class helps parse a command queue. Note that all sizes/lengths are in
+// units of uint32_t's.
+class CommandReaderBase {
+ public:
+ CommandReaderBase() : mDataMaxSize(0) { reset(); }
+
+ bool setMQDescriptor(const DescriptorType& descriptor) {
+ mQueue = std::make_unique<CommandQueueType>(descriptor, false);
+ if (mQueue->isValid()) {
+ return true;
+ } else {
+ mQueue = nullptr;
+ return false;
+ }
+ }
+
+ bool readQueue(int32_t commandLength, std::vector<NativeHandle> commandHandles) {
+ if (!mQueue) {
+ return false;
+ }
+
+ auto quantumCount = mQueue->getQuantumCount();
+ if (mDataMaxSize < quantumCount) {
+ mDataMaxSize = quantumCount;
+ mData = std::make_unique<int32_t[]>(mDataMaxSize);
+ }
+
+ if (commandLength > mDataMaxSize || !mQueue->read(mData.get(), commandLength)) {
+ ALOGE("failed to read commands from message queue");
+ return false;
+ }
+
+ mDataSize = commandLength;
+ mDataRead = 0;
+ mCommandBegin = 0;
+ mCommandEnd = 0;
+ mDataHandles = std::move(commandHandles);
+ return true;
+ }
+
+ void reset() {
+ mDataSize = 0;
+ mDataRead = 0;
+ mCommandBegin = 0;
+ mCommandEnd = 0;
+ mDataHandles.clear();
+ }
+
+ protected:
+ template <typename T>
+ bool beginCommand(T* outCommand, uint16_t* outLength) {
+ return beginCommandBase(reinterpret_cast<Command*>(outCommand), outLength);
+ }
+
+ bool isEmpty() const { return (mDataRead >= mDataSize); }
+
+ bool beginCommandBase(Command* outCommand, uint16_t* outLength) {
+ if (mCommandEnd) {
+ LOG_FATAL("endCommand was not called for last command");
+ }
+
+ constexpr uint32_t opcode_mask = static_cast<uint32_t>(Command::OPCODE_MASK);
+ constexpr uint32_t length_mask = static_cast<uint32_t>(Command::LENGTH_MASK);
+
+ uint32_t val = read();
+ *outCommand = static_cast<Command>(val & opcode_mask);
+ *outLength = static_cast<uint16_t>(val & length_mask);
+
+ if (mDataRead + *outLength > mDataSize) {
+ ALOGE("command 0x%x has invalid command length %" PRIu16, *outCommand, *outLength);
+ // undo the read() above
+ mDataRead--;
+ return false;
+ }
+
+ mCommandEnd = mDataRead + *outLength;
+
+ return true;
+ }
+
+ void endCommand() {
+ if (!mCommandEnd) {
+ LOG_FATAL("beginCommand was not called");
+ } else if (mDataRead > mCommandEnd) {
+ LOG_FATAL("too much data read");
+ mDataRead = mCommandEnd;
+ } else if (mDataRead < mCommandEnd) {
+ LOG_FATAL("too little data read");
+ mDataRead = mCommandEnd;
+ }
+
+ mCommandBegin = mCommandEnd;
+ mCommandEnd = 0;
+ }
+
+ uint32_t getCommandLoc() const { return mCommandBegin; }
+
+ uint32_t read() { return mData[mDataRead++]; }
+
+ int32_t readSigned() {
+ int32_t val;
+ memcpy(&val, &mData[mDataRead++], sizeof(val));
+ return val;
+ }
+
+ float readFloat() {
+ float val;
+ memcpy(&val, &mData[mDataRead++], sizeof(val));
+ return val;
+ }
+
+ uint64_t read64() {
+ uint32_t lo = read();
+ uint32_t hi = read();
+ return (static_cast<uint64_t>(hi) << 32) | lo;
+ }
+
+ Color readColor() {
+ uint32_t val = read();
+ return Color{
+ static_cast<int8_t>((val >> 0) & 0xff),
+ static_cast<int8_t>((val >> 8) & 0xff),
+ static_cast<int8_t>((val >> 16) & 0xff),
+ static_cast<int8_t>((val >> 24) & 0xff),
+ };
+ }
+
+ // ownership of handle is not transferred
+ const native_handle_t* readHandle(bool* outUseCache) {
+ const native_handle_t* handle = nullptr;
+
+ int32_t index = readSigned();
+ switch (index) {
+ case static_cast<int32_t>(HandleIndex::EMPTY):
+ *outUseCache = false;
+ break;
+ case static_cast<int32_t>(HandleIndex::CACHED):
+ *outUseCache = true;
+ break;
+ default:
+ if (static_cast<size_t>(index) < mDataHandles.size()) {
+ handle = ::android::makeFromAidl(mDataHandles[index]);
+ } else {
+ ALOGE("invalid handle index %zu", static_cast<size_t>(index));
+ }
+ *outUseCache = false;
+ break;
+ }
+
+ return handle;
+ }
+
+ const native_handle_t* readHandle() {
+ bool useCache;
+ return readHandle(&useCache);
+ }
+
+ // ownership of fence is transferred
+ int readFence() {
+ auto handle = readHandle();
+ if (!handle || handle->numFds == 0) {
+ return -1;
+ }
+
+ if (handle->numFds != 1) {
+ ALOGE("invalid fence handle with %d fds", handle->numFds);
+ return -1;
+ }
+
+ int fd = dup(handle->data[0]);
+ if (fd < 0) {
+ ALOGW("failed to dup fence %d", handle->data[0]);
+ sync_wait(handle->data[0], -1);
+ fd = -1;
+ }
+
+ return fd;
+ }
+
+ std::unique_ptr<int32_t[]> mData;
+ uint32_t mDataRead;
+
+ private:
+ std::unique_ptr<CommandQueueType> mQueue;
+ uint32_t mDataMaxSize;
+
+ uint32_t mDataSize;
+
+ // begin/end offsets of the current command
+ uint32_t mCommandBegin;
+ uint32_t mCommandEnd;
+
+ std::vector<NativeHandle> mDataHandles;
+};
+
+} // namespace aidl::android::hardware::graphics::composer3
diff --git a/neuralnetworks/1.0/utils/Android.bp b/neuralnetworks/1.0/utils/Android.bp
index 8c51c67..71a190e 100644
--- a/neuralnetworks/1.0/utils/Android.bp
+++ b/neuralnetworks/1.0/utils/Android.bp
@@ -49,7 +49,7 @@
static_libs: [
"android.hardware.neuralnetworks@1.0",
"libgmock",
- "libneuralnetworks_common",
+ "libneuralnetworks_common_hidl",
"neuralnetworks_types",
"neuralnetworks_utils_hal_common",
"neuralnetworks_utils_hal_1_0",
diff --git a/neuralnetworks/1.0/vts/functional/Android.bp b/neuralnetworks/1.0/vts/functional/Android.bp
index b33c581..ae9ff0a 100644
--- a/neuralnetworks/1.0/vts/functional/Android.bp
+++ b/neuralnetworks/1.0/vts/functional/Android.bp
@@ -50,7 +50,7 @@
"libgmock",
"libhidlmemory",
"libneuralnetworks_generated_test_harness",
- "libneuralnetworks_utils",
+ "libneuralnetworks_common_hidl",
],
header_libs: [
"libneuralnetworks_headers",
@@ -81,7 +81,7 @@
"libgmock",
"libhidlmemory",
"libneuralnetworks_generated_test_harness",
- "libneuralnetworks_utils",
+ "libneuralnetworks_common_hidl",
],
whole_static_libs: [
"neuralnetworks_generated_V1_0_example",
diff --git a/neuralnetworks/1.1/utils/Android.bp b/neuralnetworks/1.1/utils/Android.bp
index 737ff58..478a742 100644
--- a/neuralnetworks/1.1/utils/Android.bp
+++ b/neuralnetworks/1.1/utils/Android.bp
@@ -52,7 +52,7 @@
"android.hardware.neuralnetworks@1.0",
"android.hardware.neuralnetworks@1.1",
"libgmock",
- "libneuralnetworks_common",
+ "libneuralnetworks_common_hidl",
"neuralnetworks_types",
"neuralnetworks_utils_hal_common",
"neuralnetworks_utils_hal_1_0",
diff --git a/neuralnetworks/1.1/vts/functional/Android.bp b/neuralnetworks/1.1/vts/functional/Android.bp
index c001112..4c57788 100644
--- a/neuralnetworks/1.1/vts/functional/Android.bp
+++ b/neuralnetworks/1.1/vts/functional/Android.bp
@@ -48,7 +48,7 @@
"libgmock",
"libhidlmemory",
"libneuralnetworks_generated_test_harness",
- "libneuralnetworks_utils",
+ "libneuralnetworks_common_hidl",
],
whole_static_libs: [
"neuralnetworks_generated_V1_0_example",
diff --git a/neuralnetworks/1.2/utils/Android.bp b/neuralnetworks/1.2/utils/Android.bp
index 4eefb0f..2a86a00 100644
--- a/neuralnetworks/1.2/utils/Android.bp
+++ b/neuralnetworks/1.2/utils/Android.bp
@@ -71,7 +71,7 @@
"android.hardware.neuralnetworks@1.1",
"android.hardware.neuralnetworks@1.2",
"libgmock",
- "libneuralnetworks_common",
+ "libneuralnetworks_common_hidl",
"neuralnetworks_types",
"neuralnetworks_utils_hal_common",
"neuralnetworks_utils_hal_1_0",
diff --git a/neuralnetworks/1.2/vts/functional/Android.bp b/neuralnetworks/1.2/vts/functional/Android.bp
index e313b47..15a5d2f 100644
--- a/neuralnetworks/1.2/vts/functional/Android.bp
+++ b/neuralnetworks/1.2/vts/functional/Android.bp
@@ -71,7 +71,7 @@
"libgmock",
"libhidlmemory",
"libneuralnetworks_generated_test_harness",
- "libneuralnetworks_utils",
+ "libneuralnetworks_common_hidl",
],
whole_static_libs: [
"neuralnetworks_generated_V1_0_example",
diff --git a/neuralnetworks/1.3/utils/Android.bp b/neuralnetworks/1.3/utils/Android.bp
index 7acb4fc..8ae509f 100644
--- a/neuralnetworks/1.3/utils/Android.bp
+++ b/neuralnetworks/1.3/utils/Android.bp
@@ -69,7 +69,7 @@
"android.hardware.neuralnetworks@1.2",
"android.hardware.neuralnetworks@1.3",
"libgmock",
- "libneuralnetworks_common",
+ "libneuralnetworks_common_hidl",
"neuralnetworks_types",
"neuralnetworks_utils_hal_common",
"neuralnetworks_utils_hal_1_0",
diff --git a/neuralnetworks/1.3/vts/functional/Android.bp b/neuralnetworks/1.3/vts/functional/Android.bp
index ab0a018..ca59cc2 100644
--- a/neuralnetworks/1.3/vts/functional/Android.bp
+++ b/neuralnetworks/1.3/vts/functional/Android.bp
@@ -66,7 +66,6 @@
"VtsHalNeuralNetworksV1_0_utils",
"VtsHalNeuralNetworksV1_2_utils",
"VtsHalNeuralNetworksV1_3_utils",
- "android.hardware.neuralnetworks-V2-ndk",
"android.hardware.neuralnetworks@1.0",
"android.hardware.neuralnetworks@1.1",
"android.hardware.neuralnetworks@1.2",
@@ -76,7 +75,7 @@
"libgmock",
"libhidlmemory",
"libneuralnetworks_generated_test_harness",
- "libneuralnetworks_utils",
+ "libneuralnetworks_common_hidl",
"libsync",
],
whole_static_libs: [
diff --git a/neuralnetworks/aidl/utils/Android.bp b/neuralnetworks/aidl/utils/Android.bp
index 63cf45d..2c2419b 100644
--- a/neuralnetworks/aidl/utils/Android.bp
+++ b/neuralnetworks/aidl/utils/Android.bp
@@ -23,8 +23,8 @@
default_applicable_licenses: ["hardware_interfaces_license"],
}
-cc_library_static {
- name: "neuralnetworks_utils_hal_aidl",
+cc_defaults {
+ name: "neuralnetworks_utils_hal_aidl_defaults",
defaults: ["neuralnetworks_utils_defaults"],
srcs: ["src/*"],
local_include_dirs: ["include/nnapi/hal/aidl/"],
@@ -38,7 +38,6 @@
"neuralnetworks_utils_hal_common",
],
shared_libs: [
- "android.hardware.neuralnetworks-V2-ndk",
"libbinder_ndk",
"libhidlbase",
],
@@ -49,21 +48,49 @@
},
}
-cc_test {
- name: "neuralnetworks_utils_hal_aidl_test",
- defaults: ["neuralnetworks_utils_defaults"],
- srcs: [
- "test/*.cpp",
+cc_library_static {
+ name: "neuralnetworks_utils_hal_aidl_v1",
+ defaults: ["neuralnetworks_utils_hal_aidl_defaults"],
+ shared_libs: [
+ "android.hardware.neuralnetworks-V1-ndk",
],
+}
+
+cc_library_static {
+ name: "neuralnetworks_utils_hal_aidl_v2",
+ defaults: ["neuralnetworks_utils_hal_aidl_defaults"],
+ shared_libs: [
+ "android.hardware.neuralnetworks-V2-ndk",
+ ],
+}
+
+// A cc_defaults that includes the latest non-experimental AIDL utilities and other AIDL libraries
+// that are commonly used together. Modules that always depend on the latest non-experimental
+// AIDL features can include this cc_defaults to avoid managing dependency versions explicitly.
+cc_defaults {
+ name: "neuralnetworks_use_latest_utils_hal_aidl",
static_libs: [
"android.hardware.common-V2-ndk",
"android.hardware.graphics.common-V3-ndk",
"android.hardware.neuralnetworks-V2-ndk",
+ "neuralnetworks_utils_hal_aidl_v2",
+ ],
+}
+
+cc_test {
+ name: "neuralnetworks_utils_hal_aidl_test",
+ defaults: [
+ "neuralnetworks_use_latest_utils_hal_aidl",
+ "neuralnetworks_utils_defaults",
+ ],
+ srcs: [
+ "test/*.cpp",
+ ],
+ static_libs: [
"libaidlcommonsupport",
"libgmock",
"libneuralnetworks_common",
"neuralnetworks_types",
- "neuralnetworks_utils_hal_aidl",
"neuralnetworks_utils_hal_common",
],
shared_libs: [
diff --git a/neuralnetworks/aidl/utils/include/nnapi/hal/aidl/Device.h b/neuralnetworks/aidl/utils/include/nnapi/hal/aidl/Device.h
index 1457646..d558f66 100644
--- a/neuralnetworks/aidl/utils/include/nnapi/hal/aidl/Device.h
+++ b/neuralnetworks/aidl/utils/include/nnapi/hal/aidl/Device.h
@@ -43,11 +43,12 @@
public:
static nn::GeneralResult<std::shared_ptr<const Device>> create(
- std::string name, std::shared_ptr<aidl_hal::IDevice> device);
+ std::string name, std::shared_ptr<aidl_hal::IDevice> device, nn::Version featureLevel);
Device(PrivateConstructorTag tag, std::string name, std::string versionString,
- nn::DeviceType deviceType, std::vector<nn::Extension> extensions,
- nn::Capabilities capabilities, std::pair<uint32_t, uint32_t> numberOfCacheFilesNeeded,
+ nn::Version featureLevel, nn::DeviceType deviceType,
+ std::vector<nn::Extension> extensions, nn::Capabilities capabilities,
+ std::pair<uint32_t, uint32_t> numberOfCacheFilesNeeded,
std::shared_ptr<aidl_hal::IDevice> device, DeathHandler deathHandler);
const std::string& getName() const override;
@@ -84,6 +85,7 @@
private:
const std::string kName;
const std::string kVersionString;
+ const nn::Version kFeatureLevel;
const nn::DeviceType kDeviceType;
const std::vector<nn::Extension> kExtensions;
const nn::Capabilities kCapabilities;
diff --git a/neuralnetworks/aidl/utils/src/Device.cpp b/neuralnetworks/aidl/utils/src/Device.cpp
index e80de0b..5b7ec4e 100644
--- a/neuralnetworks/aidl/utils/src/Device.cpp
+++ b/neuralnetworks/aidl/utils/src/Device.cpp
@@ -125,7 +125,7 @@
} // namespace
nn::GeneralResult<std::shared_ptr<const Device>> Device::create(
- std::string name, std::shared_ptr<aidl_hal::IDevice> device) {
+ std::string name, std::shared_ptr<aidl_hal::IDevice> device, nn::Version featureLevel) {
if (name.empty()) {
return NN_ERROR(nn::ErrorStatus::INVALID_ARGUMENT)
<< "aidl_hal::utils::Device::create must have non-empty name";
@@ -143,18 +143,19 @@
auto deathHandler = NN_TRY(DeathHandler::create(device));
return std::make_shared<const Device>(
- PrivateConstructorTag{}, std::move(name), std::move(versionString), deviceType,
- std::move(extensions), std::move(capabilities), numberOfCacheFilesNeeded,
+ PrivateConstructorTag{}, std::move(name), std::move(versionString), featureLevel,
+ deviceType, std::move(extensions), std::move(capabilities), numberOfCacheFilesNeeded,
std::move(device), std::move(deathHandler));
}
Device::Device(PrivateConstructorTag /*tag*/, std::string name, std::string versionString,
- nn::DeviceType deviceType, std::vector<nn::Extension> extensions,
- nn::Capabilities capabilities,
+ nn::Version featureLevel, nn::DeviceType deviceType,
+ std::vector<nn::Extension> extensions, nn::Capabilities capabilities,
std::pair<uint32_t, uint32_t> numberOfCacheFilesNeeded,
std::shared_ptr<aidl_hal::IDevice> device, DeathHandler deathHandler)
: kName(std::move(name)),
kVersionString(std::move(versionString)),
+ kFeatureLevel(featureLevel),
kDeviceType(deviceType),
kExtensions(std::move(extensions)),
kCapabilities(std::move(capabilities)),
@@ -171,7 +172,7 @@
}
nn::Version Device::getFeatureLevel() const {
- return nn::Version::ANDROID_S;
+ return kFeatureLevel;
}
nn::DeviceType Device::getType() const {
diff --git a/neuralnetworks/aidl/utils/src/Service.cpp b/neuralnetworks/aidl/utils/src/Service.cpp
index ac182a2..01772ee 100644
--- a/neuralnetworks/aidl/utils/src/Service.cpp
+++ b/neuralnetworks/aidl/utils/src/Service.cpp
@@ -17,6 +17,7 @@
#include "Service.h"
#include <AndroidVersionUtil.h>
+#include <aidl/android/hardware/neuralnetworks/IDevice.h>
#include <android/binder_auto_utils.h>
#include <android/binder_manager.h>
#include <android/binder_process.h>
@@ -28,8 +29,33 @@
#include <string>
#include "Device.h"
+#include "Utils.h"
namespace aidl::android::hardware::neuralnetworks::utils {
+namespace {
+
+// Map the AIDL version of an IDevice to NNAPI canonical feature level.
+nn::GeneralResult<nn::Version> getAidlServiceFeatureLevel(IDevice* service) {
+ CHECK(service != nullptr);
+ int aidlVersion;
+ const auto ret = service->getInterfaceVersion(&aidlVersion);
+ HANDLE_ASTATUS(ret) << "getInterfaceVersion failed";
+
+ // For service AIDL versions greater than or equal to the AIDL library version that the runtime
+ // was built against, clamp it to the runtime AIDL library version.
+ aidlVersion = std::min(aidlVersion, IDevice::version);
+
+ // Map stable AIDL versions to canonical versions.
+ switch (aidlVersion) {
+ case 1:
+ return nn::Version::ANDROID_S;
+ case 2:
+ return nn::Version::FEATURE_LEVEL_6;
+ }
+ return NN_ERROR() << "Unknown AIDL service version: " << aidlVersion;
+}
+
+} // namespace
nn::GeneralResult<nn::SharedDevice> getDevice(const std::string& instanceName) {
auto fullName = std::string(IDevice::descriptor) + "/" + instanceName;
@@ -55,7 +81,8 @@
<< " returned nullptr";
}
ABinderProcess_startThreadPool();
- return Device::create(instanceName, std::move(service));
+ const auto featureLevel = NN_TRY(getAidlServiceFeatureLevel(service.get()));
+ return Device::create(instanceName, std::move(service), featureLevel);
};
return hal::utils::ResilientDevice::create(std::move(makeDevice));
diff --git a/neuralnetworks/aidl/utils/test/DeviceTest.cpp b/neuralnetworks/aidl/utils/test/DeviceTest.cpp
index f121aca..79abe1b 100644
--- a/neuralnetworks/aidl/utils/test/DeviceTest.cpp
+++ b/neuralnetworks/aidl/utils/test/DeviceTest.cpp
@@ -146,28 +146,45 @@
return ndk::ScopedAStatus::fromStatus(STATUS_DEAD_OBJECT);
};
+class DeviceTest : public ::testing::TestWithParam<nn::Version> {
+ protected:
+ const nn::Version kVersion = GetParam();
+};
+
+std::string printDeviceTest(const testing::TestParamInfo<nn::Version>& info) {
+ switch (info.param) {
+ case nn::Version::ANDROID_S:
+ return "v1";
+ case nn::Version::FEATURE_LEVEL_6:
+ return "v2";
+ default:
+ LOG(FATAL) << "Invalid AIDL version: " << info.param;
+ return "invalid";
+ }
+}
+
} // namespace
-TEST(DeviceTest, invalidName) {
+TEST_P(DeviceTest, invalidName) {
// run test
const auto device = MockDevice::create();
- const auto result = Device::create(kInvalidName, device);
+ const auto result = Device::create(kInvalidName, device, kVersion);
// verify result
ASSERT_FALSE(result.has_value());
EXPECT_EQ(result.error().code, nn::ErrorStatus::INVALID_ARGUMENT);
}
-TEST(DeviceTest, invalidDevice) {
+TEST_P(DeviceTest, invalidDevice) {
// run test
- const auto result = Device::create(kName, kInvalidDevice);
+ const auto result = Device::create(kName, kInvalidDevice, kVersion);
// verify result
ASSERT_FALSE(result.has_value());
EXPECT_EQ(result.error().code, nn::ErrorStatus::INVALID_ARGUMENT);
}
-TEST(DeviceTest, getVersionStringError) {
+TEST_P(DeviceTest, getVersionStringError) {
// setup call
const auto mockDevice = createMockDevice();
EXPECT_CALL(*mockDevice, getVersionString(_))
@@ -175,14 +192,14 @@
.WillOnce(InvokeWithoutArgs(makeGeneralFailure));
// run test
- const auto result = Device::create(kName, mockDevice);
+ const auto result = Device::create(kName, mockDevice, kVersion);
// verify result
ASSERT_FALSE(result.has_value());
EXPECT_EQ(result.error().code, nn::ErrorStatus::GENERAL_FAILURE);
}
-TEST(DeviceTest, getVersionStringTransportFailure) {
+TEST_P(DeviceTest, getVersionStringTransportFailure) {
// setup call
const auto mockDevice = createMockDevice();
EXPECT_CALL(*mockDevice, getVersionString(_))
@@ -190,14 +207,14 @@
.WillOnce(InvokeWithoutArgs(makeGeneralTransportFailure));
// run test
- const auto result = Device::create(kName, mockDevice);
+ const auto result = Device::create(kName, mockDevice, kVersion);
// verify result
ASSERT_FALSE(result.has_value());
EXPECT_EQ(result.error().code, nn::ErrorStatus::GENERAL_FAILURE);
}
-TEST(DeviceTest, getVersionStringDeadObject) {
+TEST_P(DeviceTest, getVersionStringDeadObject) {
// setup call
const auto mockDevice = createMockDevice();
EXPECT_CALL(*mockDevice, getVersionString(_))
@@ -205,27 +222,27 @@
.WillOnce(InvokeWithoutArgs(makeDeadObjectFailure));
// run test
- const auto result = Device::create(kName, mockDevice);
+ const auto result = Device::create(kName, mockDevice, kVersion);
// verify result
ASSERT_FALSE(result.has_value());
EXPECT_EQ(result.error().code, nn::ErrorStatus::DEAD_OBJECT);
}
-TEST(DeviceTest, getTypeError) {
+TEST_P(DeviceTest, getTypeError) {
// setup call
const auto mockDevice = createMockDevice();
EXPECT_CALL(*mockDevice, getType(_)).Times(1).WillOnce(InvokeWithoutArgs(makeGeneralFailure));
// run test
- const auto result = Device::create(kName, mockDevice);
+ const auto result = Device::create(kName, mockDevice, kVersion);
// verify result
ASSERT_FALSE(result.has_value());
EXPECT_EQ(result.error().code, nn::ErrorStatus::GENERAL_FAILURE);
}
-TEST(DeviceTest, getTypeTransportFailure) {
+TEST_P(DeviceTest, getTypeTransportFailure) {
// setup call
const auto mockDevice = createMockDevice();
EXPECT_CALL(*mockDevice, getType(_))
@@ -233,14 +250,14 @@
.WillOnce(InvokeWithoutArgs(makeGeneralTransportFailure));
// run test
- const auto result = Device::create(kName, mockDevice);
+ const auto result = Device::create(kName, mockDevice, kVersion);
// verify result
ASSERT_FALSE(result.has_value());
EXPECT_EQ(result.error().code, nn::ErrorStatus::GENERAL_FAILURE);
}
-TEST(DeviceTest, getTypeDeadObject) {
+TEST_P(DeviceTest, getTypeDeadObject) {
// setup call
const auto mockDevice = createMockDevice();
EXPECT_CALL(*mockDevice, getType(_))
@@ -248,14 +265,14 @@
.WillOnce(InvokeWithoutArgs(makeDeadObjectFailure));
// run test
- const auto result = Device::create(kName, mockDevice);
+ const auto result = Device::create(kName, mockDevice, kVersion);
// verify result
ASSERT_FALSE(result.has_value());
EXPECT_EQ(result.error().code, nn::ErrorStatus::DEAD_OBJECT);
}
-TEST(DeviceTest, getSupportedExtensionsError) {
+TEST_P(DeviceTest, getSupportedExtensionsError) {
// setup call
const auto mockDevice = createMockDevice();
EXPECT_CALL(*mockDevice, getSupportedExtensions(_))
@@ -263,14 +280,14 @@
.WillOnce(InvokeWithoutArgs(makeGeneralFailure));
// run test
- const auto result = Device::create(kName, mockDevice);
+ const auto result = Device::create(kName, mockDevice, kVersion);
// verify result
ASSERT_FALSE(result.has_value());
EXPECT_EQ(result.error().code, nn::ErrorStatus::GENERAL_FAILURE);
}
-TEST(DeviceTest, getSupportedExtensionsTransportFailure) {
+TEST_P(DeviceTest, getSupportedExtensionsTransportFailure) {
// setup call
const auto mockDevice = createMockDevice();
EXPECT_CALL(*mockDevice, getSupportedExtensions(_))
@@ -278,14 +295,14 @@
.WillOnce(InvokeWithoutArgs(makeGeneralTransportFailure));
// run test
- const auto result = Device::create(kName, mockDevice);
+ const auto result = Device::create(kName, mockDevice, kVersion);
// verify result
ASSERT_FALSE(result.has_value());
EXPECT_EQ(result.error().code, nn::ErrorStatus::GENERAL_FAILURE);
}
-TEST(DeviceTest, getSupportedExtensionsDeadObject) {
+TEST_P(DeviceTest, getSupportedExtensionsDeadObject) {
// setup call
const auto mockDevice = createMockDevice();
EXPECT_CALL(*mockDevice, getSupportedExtensions(_))
@@ -293,20 +310,20 @@
.WillOnce(InvokeWithoutArgs(makeDeadObjectFailure));
// run test
- const auto result = Device::create(kName, mockDevice);
+ const auto result = Device::create(kName, mockDevice, kVersion);
// verify result
ASSERT_FALSE(result.has_value());
EXPECT_EQ(result.error().code, nn::ErrorStatus::DEAD_OBJECT);
}
-TEST(DeviceTest, getNumberOfCacheFilesNeeded) {
+TEST_P(DeviceTest, getNumberOfCacheFilesNeeded) {
// setup call
const auto mockDevice = createMockDevice();
EXPECT_CALL(*mockDevice, getNumberOfCacheFilesNeeded(_)).Times(1);
// run test
- const auto result = Device::create(kName, mockDevice);
+ const auto result = Device::create(kName, mockDevice, kVersion);
// verify result
ASSERT_TRUE(result.has_value());
@@ -315,7 +332,7 @@
EXPECT_EQ(result.value()->getNumberOfCacheFilesNeeded(), kNumberOfCacheFilesPair);
}
-TEST(DeviceTest, getNumberOfCacheFilesNeededError) {
+TEST_P(DeviceTest, getNumberOfCacheFilesNeededError) {
// setup call
const auto mockDevice = createMockDevice();
EXPECT_CALL(*mockDevice, getNumberOfCacheFilesNeeded(_))
@@ -323,14 +340,14 @@
.WillOnce(InvokeWithoutArgs(makeGeneralFailure));
// run test
- const auto result = Device::create(kName, mockDevice);
+ const auto result = Device::create(kName, mockDevice, kVersion);
// verify result
ASSERT_FALSE(result.has_value());
EXPECT_EQ(result.error().code, nn::ErrorStatus::GENERAL_FAILURE);
}
-TEST(DeviceTest, dataCacheFilesExceedsSpecifiedMax) {
+TEST_P(DeviceTest, dataCacheFilesExceedsSpecifiedMax) {
// setup test
const auto mockDevice = createMockDevice();
EXPECT_CALL(*mockDevice, getNumberOfCacheFilesNeeded(_))
@@ -341,14 +358,14 @@
InvokeWithoutArgs(makeStatusOk)));
// run test
- const auto result = Device::create(kName, mockDevice);
+ const auto result = Device::create(kName, mockDevice, kVersion);
// verify result
ASSERT_FALSE(result.has_value());
EXPECT_EQ(result.error().code, nn::ErrorStatus::GENERAL_FAILURE);
}
-TEST(DeviceTest, modelCacheFilesExceedsSpecifiedMax) {
+TEST_P(DeviceTest, modelCacheFilesExceedsSpecifiedMax) {
// setup test
const auto mockDevice = createMockDevice();
EXPECT_CALL(*mockDevice, getNumberOfCacheFilesNeeded(_))
@@ -359,14 +376,14 @@
InvokeWithoutArgs(makeStatusOk)));
// run test
- const auto result = Device::create(kName, mockDevice);
+ const auto result = Device::create(kName, mockDevice, kVersion);
// verify result
ASSERT_FALSE(result.has_value());
EXPECT_EQ(result.error().code, nn::ErrorStatus::GENERAL_FAILURE);
}
-TEST(DeviceTest, getNumberOfCacheFilesNeededTransportFailure) {
+TEST_P(DeviceTest, getNumberOfCacheFilesNeededTransportFailure) {
// setup call
const auto mockDevice = createMockDevice();
EXPECT_CALL(*mockDevice, getNumberOfCacheFilesNeeded(_))
@@ -374,14 +391,14 @@
.WillOnce(InvokeWithoutArgs(makeGeneralTransportFailure));
// run test
- const auto result = Device::create(kName, mockDevice);
+ const auto result = Device::create(kName, mockDevice, kVersion);
// verify result
ASSERT_FALSE(result.has_value());
EXPECT_EQ(result.error().code, nn::ErrorStatus::GENERAL_FAILURE);
}
-TEST(DeviceTest, getNumberOfCacheFilesNeededDeadObject) {
+TEST_P(DeviceTest, getNumberOfCacheFilesNeededDeadObject) {
// setup call
const auto mockDevice = createMockDevice();
EXPECT_CALL(*mockDevice, getNumberOfCacheFilesNeeded(_))
@@ -389,14 +406,14 @@
.WillOnce(InvokeWithoutArgs(makeDeadObjectFailure));
// run test
- const auto result = Device::create(kName, mockDevice);
+ const auto result = Device::create(kName, mockDevice, kVersion);
// verify result
ASSERT_FALSE(result.has_value());
EXPECT_EQ(result.error().code, nn::ErrorStatus::DEAD_OBJECT);
}
-TEST(DeviceTest, getCapabilitiesError) {
+TEST_P(DeviceTest, getCapabilitiesError) {
// setup call
const auto mockDevice = createMockDevice();
EXPECT_CALL(*mockDevice, getCapabilities(_))
@@ -404,14 +421,14 @@
.WillOnce(InvokeWithoutArgs(makeGeneralFailure));
// run test
- const auto result = Device::create(kName, mockDevice);
+ const auto result = Device::create(kName, mockDevice, kVersion);
// verify result
ASSERT_FALSE(result.has_value());
EXPECT_EQ(result.error().code, nn::ErrorStatus::GENERAL_FAILURE);
}
-TEST(DeviceTest, getCapabilitiesTransportFailure) {
+TEST_P(DeviceTest, getCapabilitiesTransportFailure) {
// setup call
const auto mockDevice = createMockDevice();
EXPECT_CALL(*mockDevice, getCapabilities(_))
@@ -419,14 +436,14 @@
.WillOnce(InvokeWithoutArgs(makeGeneralTransportFailure));
// run test
- const auto result = Device::create(kName, mockDevice);
+ const auto result = Device::create(kName, mockDevice, kVersion);
// verify result
ASSERT_FALSE(result.has_value());
EXPECT_EQ(result.error().code, nn::ErrorStatus::GENERAL_FAILURE);
}
-TEST(DeviceTest, getCapabilitiesDeadObject) {
+TEST_P(DeviceTest, getCapabilitiesDeadObject) {
// setup call
const auto mockDevice = createMockDevice();
EXPECT_CALL(*mockDevice, getCapabilities(_))
@@ -434,17 +451,17 @@
.WillOnce(InvokeWithoutArgs(makeDeadObjectFailure));
// run test
- const auto result = Device::create(kName, mockDevice);
+ const auto result = Device::create(kName, mockDevice, kVersion);
// verify result
ASSERT_FALSE(result.has_value());
EXPECT_EQ(result.error().code, nn::ErrorStatus::DEAD_OBJECT);
}
-TEST(DeviceTest, getName) {
+TEST_P(DeviceTest, getName) {
// setup call
const auto mockDevice = createMockDevice();
- const auto device = Device::create(kName, mockDevice).value();
+ const auto device = Device::create(kName, mockDevice, kVersion).value();
// run test
const auto& name = device->getName();
@@ -453,19 +470,19 @@
EXPECT_EQ(name, kName);
}
-TEST(DeviceTest, getFeatureLevel) {
+TEST_P(DeviceTest, getFeatureLevel) {
// setup call
const auto mockDevice = createMockDevice();
- const auto device = Device::create(kName, mockDevice).value();
+ const auto device = Device::create(kName, mockDevice, kVersion).value();
// run test
const auto featureLevel = device->getFeatureLevel();
// verify result
- EXPECT_EQ(featureLevel, nn::Version::ANDROID_S);
+ EXPECT_EQ(featureLevel, kVersion);
}
-TEST(DeviceTest, getCachedData) {
+TEST_P(DeviceTest, getCachedData) {
// setup call
const auto mockDevice = createMockDevice();
EXPECT_CALL(*mockDevice, getVersionString(_)).Times(1);
@@ -474,7 +491,7 @@
EXPECT_CALL(*mockDevice, getNumberOfCacheFilesNeeded(_)).Times(1);
EXPECT_CALL(*mockDevice, getCapabilities(_)).Times(1);
- const auto result = Device::create(kName, mockDevice);
+ const auto result = Device::create(kName, mockDevice, kVersion);
ASSERT_TRUE(result.has_value())
<< "Failed with " << result.error().code << ": " << result.error().message;
const auto& device = result.value();
@@ -487,10 +504,10 @@
EXPECT_EQ(device->getCapabilities(), device->getCapabilities());
}
-TEST(DeviceTest, getSupportedOperations) {
+TEST_P(DeviceTest, getSupportedOperations) {
// setup call
const auto mockDevice = createMockDevice();
- const auto device = Device::create(kName, mockDevice).value();
+ const auto device = Device::create(kName, mockDevice, kVersion).value();
EXPECT_CALL(*mockDevice, getSupportedOperations(_, _))
.Times(1)
.WillOnce(DoAll(
@@ -508,10 +525,10 @@
EXPECT_THAT(supportedOperations, Each(testing::IsTrue()));
}
-TEST(DeviceTest, getSupportedOperationsError) {
+TEST_P(DeviceTest, getSupportedOperationsError) {
// setup call
const auto mockDevice = createMockDevice();
- const auto device = Device::create(kName, mockDevice).value();
+ const auto device = Device::create(kName, mockDevice, kVersion).value();
EXPECT_CALL(*mockDevice, getSupportedOperations(_, _))
.Times(1)
.WillOnce(InvokeWithoutArgs(makeGeneralFailure));
@@ -524,10 +541,10 @@
EXPECT_EQ(result.error().code, nn::ErrorStatus::GENERAL_FAILURE);
}
-TEST(DeviceTest, getSupportedOperationsTransportFailure) {
+TEST_P(DeviceTest, getSupportedOperationsTransportFailure) {
// setup call
const auto mockDevice = createMockDevice();
- const auto device = Device::create(kName, mockDevice).value();
+ const auto device = Device::create(kName, mockDevice, kVersion).value();
EXPECT_CALL(*mockDevice, getSupportedOperations(_, _))
.Times(1)
.WillOnce(InvokeWithoutArgs(makeGeneralTransportFailure));
@@ -540,10 +557,10 @@
EXPECT_EQ(result.error().code, nn::ErrorStatus::GENERAL_FAILURE);
}
-TEST(DeviceTest, getSupportedOperationsDeadObject) {
+TEST_P(DeviceTest, getSupportedOperationsDeadObject) {
// setup call
const auto mockDevice = createMockDevice();
- const auto device = Device::create(kName, mockDevice).value();
+ const auto device = Device::create(kName, mockDevice, kVersion).value();
EXPECT_CALL(*mockDevice, getSupportedOperations(_, _))
.Times(1)
.WillOnce(InvokeWithoutArgs(makeDeadObjectFailure));
@@ -556,10 +573,10 @@
EXPECT_EQ(result.error().code, nn::ErrorStatus::DEAD_OBJECT);
}
-TEST(DeviceTest, prepareModel) {
+TEST_P(DeviceTest, prepareModel) {
// setup call
const auto mockDevice = createMockDevice();
- const auto device = Device::create(kName, mockDevice).value();
+ const auto device = Device::create(kName, mockDevice, kVersion).value();
const auto mockPreparedModel = MockPreparedModel::create();
EXPECT_CALL(*mockDevice, prepareModel(_, _, _, _, _, _, _, _))
.Times(1)
@@ -576,10 +593,10 @@
EXPECT_NE(result.value(), nullptr);
}
-TEST(DeviceTest, prepareModelLaunchError) {
+TEST_P(DeviceTest, prepareModelLaunchError) {
// setup call
const auto mockDevice = createMockDevice();
- const auto device = Device::create(kName, mockDevice).value();
+ const auto device = Device::create(kName, mockDevice, kVersion).value();
EXPECT_CALL(*mockDevice, prepareModel(_, _, _, _, _, _, _, _))
.Times(1)
.WillOnce(Invoke(makePreparedModelReturn(ErrorStatus::GENERAL_FAILURE,
@@ -594,10 +611,10 @@
EXPECT_EQ(result.error().code, nn::ErrorStatus::GENERAL_FAILURE);
}
-TEST(DeviceTest, prepareModelReturnError) {
+TEST_P(DeviceTest, prepareModelReturnError) {
// setup call
const auto mockDevice = createMockDevice();
- const auto device = Device::create(kName, mockDevice).value();
+ const auto device = Device::create(kName, mockDevice, kVersion).value();
EXPECT_CALL(*mockDevice, prepareModel(_, _, _, _, _, _, _, _))
.Times(1)
.WillOnce(Invoke(makePreparedModelReturn(ErrorStatus::NONE,
@@ -612,10 +629,10 @@
EXPECT_EQ(result.error().code, nn::ErrorStatus::GENERAL_FAILURE);
}
-TEST(DeviceTest, prepareModelNullptrError) {
+TEST_P(DeviceTest, prepareModelNullptrError) {
// setup call
const auto mockDevice = createMockDevice();
- const auto device = Device::create(kName, mockDevice).value();
+ const auto device = Device::create(kName, mockDevice, kVersion).value();
EXPECT_CALL(*mockDevice, prepareModel(_, _, _, _, _, _, _, _))
.Times(1)
.WillOnce(
@@ -630,10 +647,10 @@
EXPECT_EQ(result.error().code, nn::ErrorStatus::GENERAL_FAILURE);
}
-TEST(DeviceTest, prepareModelTransportFailure) {
+TEST_P(DeviceTest, prepareModelTransportFailure) {
// setup call
const auto mockDevice = createMockDevice();
- const auto device = Device::create(kName, mockDevice).value();
+ const auto device = Device::create(kName, mockDevice, kVersion).value();
EXPECT_CALL(*mockDevice, prepareModel(_, _, _, _, _, _, _, _))
.Times(1)
.WillOnce(InvokeWithoutArgs(makeGeneralTransportFailure));
@@ -647,10 +664,10 @@
EXPECT_EQ(result.error().code, nn::ErrorStatus::GENERAL_FAILURE);
}
-TEST(DeviceTest, prepareModelDeadObject) {
+TEST_P(DeviceTest, prepareModelDeadObject) {
// setup call
const auto mockDevice = createMockDevice();
- const auto device = Device::create(kName, mockDevice).value();
+ const auto device = Device::create(kName, mockDevice, kVersion).value();
EXPECT_CALL(*mockDevice, prepareModel(_, _, _, _, _, _, _, _))
.Times(1)
.WillOnce(InvokeWithoutArgs(makeDeadObjectFailure));
@@ -664,10 +681,10 @@
EXPECT_EQ(result.error().code, nn::ErrorStatus::DEAD_OBJECT);
}
-TEST(DeviceTest, prepareModelAsyncCrash) {
+TEST_P(DeviceTest, prepareModelAsyncCrash) {
// setup test
const auto mockDevice = createMockDevice();
- const auto device = Device::create(kName, mockDevice).value();
+ const auto device = Device::create(kName, mockDevice, kVersion).value();
const auto ret = [&device]() {
DeathMonitor::serviceDied(device->getDeathMonitor());
return ndk::ScopedAStatus::ok();
@@ -685,10 +702,10 @@
EXPECT_EQ(result.error().code, nn::ErrorStatus::DEAD_OBJECT);
}
-TEST(DeviceTest, prepareModelFromCache) {
+TEST_P(DeviceTest, prepareModelFromCache) {
// setup call
const auto mockDevice = createMockDevice();
- const auto device = Device::create(kName, mockDevice).value();
+ const auto device = Device::create(kName, mockDevice, kVersion).value();
const auto mockPreparedModel = MockPreparedModel::create();
EXPECT_CALL(*mockDevice, prepareModelFromCache(_, _, _, _, _))
.Times(1)
@@ -704,10 +721,10 @@
EXPECT_NE(result.value(), nullptr);
}
-TEST(DeviceTest, prepareModelFromCacheLaunchError) {
+TEST_P(DeviceTest, prepareModelFromCacheLaunchError) {
// setup call
const auto mockDevice = createMockDevice();
- const auto device = Device::create(kName, mockDevice).value();
+ const auto device = Device::create(kName, mockDevice, kVersion).value();
EXPECT_CALL(*mockDevice, prepareModelFromCache(_, _, _, _, _))
.Times(1)
.WillOnce(Invoke(makePreparedModelFromCacheReturn(
@@ -721,10 +738,10 @@
EXPECT_EQ(result.error().code, nn::ErrorStatus::GENERAL_FAILURE);
}
-TEST(DeviceTest, prepareModelFromCacheReturnError) {
+TEST_P(DeviceTest, prepareModelFromCacheReturnError) {
// setup call
const auto mockDevice = createMockDevice();
- const auto device = Device::create(kName, mockDevice).value();
+ const auto device = Device::create(kName, mockDevice, kVersion).value();
EXPECT_CALL(*mockDevice, prepareModelFromCache(_, _, _, _, _))
.Times(1)
.WillOnce(Invoke(makePreparedModelFromCacheReturn(
@@ -738,10 +755,10 @@
EXPECT_EQ(result.error().code, nn::ErrorStatus::GENERAL_FAILURE);
}
-TEST(DeviceTest, prepareModelFromCacheNullptrError) {
+TEST_P(DeviceTest, prepareModelFromCacheNullptrError) {
// setup call
const auto mockDevice = createMockDevice();
- const auto device = Device::create(kName, mockDevice).value();
+ const auto device = Device::create(kName, mockDevice, kVersion).value();
EXPECT_CALL(*mockDevice, prepareModelFromCache(_, _, _, _, _))
.Times(1)
.WillOnce(Invoke(makePreparedModelFromCacheReturn(ErrorStatus::NONE, ErrorStatus::NONE,
@@ -755,10 +772,10 @@
EXPECT_EQ(result.error().code, nn::ErrorStatus::GENERAL_FAILURE);
}
-TEST(DeviceTest, prepareModelFromCacheTransportFailure) {
+TEST_P(DeviceTest, prepareModelFromCacheTransportFailure) {
// setup call
const auto mockDevice = createMockDevice();
- const auto device = Device::create(kName, mockDevice).value();
+ const auto device = Device::create(kName, mockDevice, kVersion).value();
EXPECT_CALL(*mockDevice, prepareModelFromCache(_, _, _, _, _))
.Times(1)
.WillOnce(InvokeWithoutArgs(makeGeneralTransportFailure));
@@ -771,10 +788,10 @@
EXPECT_EQ(result.error().code, nn::ErrorStatus::GENERAL_FAILURE);
}
-TEST(DeviceTest, prepareModelFromCacheDeadObject) {
+TEST_P(DeviceTest, prepareModelFromCacheDeadObject) {
// setup call
const auto mockDevice = createMockDevice();
- const auto device = Device::create(kName, mockDevice).value();
+ const auto device = Device::create(kName, mockDevice, kVersion).value();
EXPECT_CALL(*mockDevice, prepareModelFromCache(_, _, _, _, _))
.Times(1)
.WillOnce(InvokeWithoutArgs(makeDeadObjectFailure));
@@ -787,10 +804,10 @@
EXPECT_EQ(result.error().code, nn::ErrorStatus::DEAD_OBJECT);
}
-TEST(DeviceTest, prepareModelFromCacheAsyncCrash) {
+TEST_P(DeviceTest, prepareModelFromCacheAsyncCrash) {
// setup test
const auto mockDevice = createMockDevice();
- const auto device = Device::create(kName, mockDevice).value();
+ const auto device = Device::create(kName, mockDevice, kVersion).value();
const auto ret = [&device]() {
DeathMonitor::serviceDied(device->getDeathMonitor());
return ndk::ScopedAStatus::ok();
@@ -807,10 +824,10 @@
EXPECT_EQ(result.error().code, nn::ErrorStatus::DEAD_OBJECT);
}
-TEST(DeviceTest, allocate) {
+TEST_P(DeviceTest, allocate) {
// setup call
const auto mockDevice = createMockDevice();
- const auto device = Device::create(kName, mockDevice).value();
+ const auto device = Device::create(kName, mockDevice, kVersion).value();
const auto mockBuffer = DeviceBuffer{.buffer = MockBuffer::create(), .token = 1};
EXPECT_CALL(*mockDevice, allocate(_, _, _, _, _))
.Times(1)
@@ -825,10 +842,10 @@
EXPECT_NE(result.value(), nullptr);
}
-TEST(DeviceTest, allocateError) {
+TEST_P(DeviceTest, allocateError) {
// setup call
const auto mockDevice = createMockDevice();
- const auto device = Device::create(kName, mockDevice).value();
+ const auto device = Device::create(kName, mockDevice, kVersion).value();
EXPECT_CALL(*mockDevice, allocate(_, _, _, _, _))
.Times(1)
.WillOnce(InvokeWithoutArgs(makeGeneralFailure));
@@ -841,10 +858,10 @@
EXPECT_EQ(result.error().code, nn::ErrorStatus::GENERAL_FAILURE);
}
-TEST(DeviceTest, allocateTransportFailure) {
+TEST_P(DeviceTest, allocateTransportFailure) {
// setup call
const auto mockDevice = createMockDevice();
- const auto device = Device::create(kName, mockDevice).value();
+ const auto device = Device::create(kName, mockDevice, kVersion).value();
EXPECT_CALL(*mockDevice, allocate(_, _, _, _, _))
.Times(1)
.WillOnce(InvokeWithoutArgs(makeGeneralTransportFailure));
@@ -857,10 +874,10 @@
EXPECT_EQ(result.error().code, nn::ErrorStatus::GENERAL_FAILURE);
}
-TEST(DeviceTest, allocateDeadObject) {
+TEST_P(DeviceTest, allocateDeadObject) {
// setup call
const auto mockDevice = createMockDevice();
- const auto device = Device::create(kName, mockDevice).value();
+ const auto device = Device::create(kName, mockDevice, kVersion).value();
EXPECT_CALL(*mockDevice, allocate(_, _, _, _, _))
.Times(1)
.WillOnce(InvokeWithoutArgs(makeDeadObjectFailure));
@@ -873,4 +890,8 @@
EXPECT_EQ(result.error().code, nn::ErrorStatus::DEAD_OBJECT);
}
+INSTANTIATE_TEST_SUITE_P(TestDevice, DeviceTest,
+ ::testing::Values(nn::Version::ANDROID_S, nn::Version::FEATURE_LEVEL_6),
+ printDeviceTest);
+
} // namespace aidl::android::hardware::neuralnetworks::utils
diff --git a/neuralnetworks/aidl/vts/functional/Android.bp b/neuralnetworks/aidl/vts/functional/Android.bp
index f3404a9..8fd8f4f 100644
--- a/neuralnetworks/aidl/vts/functional/Android.bp
+++ b/neuralnetworks/aidl/vts/functional/Android.bp
@@ -26,6 +26,7 @@
cc_test {
name: "VtsHalNeuralnetworksTargetTest",
defaults: [
+ "neuralnetworks_use_latest_utils_hal_aidl",
"neuralnetworks_vts_functional_defaults",
"use_libaidlvintf_gtest_helper_static",
],
@@ -51,16 +52,14 @@
static_libs: [
"android.hardware.common-V2-ndk",
"android.hardware.graphics.common-V3-ndk",
- "android.hardware.neuralnetworks-V2-ndk",
"android.hidl.allocator@1.0",
"android.hidl.memory@1.0",
"libaidlcommonsupport",
"libgmock",
"libhidlmemory",
+ "libneuralnetworks_common",
"libneuralnetworks_generated_test_harness",
- "libneuralnetworks_utils",
"libsync",
- "neuralnetworks_utils_hal_aidl",
],
whole_static_libs: [
"neuralnetworks_generated_V1_0_example",
diff --git a/neuralnetworks/utils/common/Android.bp b/neuralnetworks/utils/common/Android.bp
index f88e407..c0645b0 100644
--- a/neuralnetworks/utils/common/Android.bp
+++ b/neuralnetworks/utils/common/Android.bp
@@ -35,7 +35,6 @@
"neuralnetworks_types",
],
shared_libs: [
- "android.hardware.neuralnetworks-V2-ndk",
"libhidlbase",
"libbinder_ndk",
],
@@ -53,7 +52,7 @@
static_libs: [
"android.hardware.neuralnetworks@1.0",
"libgmock",
- "libneuralnetworks_common",
+ "libneuralnetworks_common_hidl",
"neuralnetworks_types",
"neuralnetworks_utils_hal_common",
],
diff --git a/neuralnetworks/utils/service/Android.bp b/neuralnetworks/utils/service/Android.bp
index fbb8679..3288d05 100644
--- a/neuralnetworks/utils/service/Android.bp
+++ b/neuralnetworks/utils/service/Android.bp
@@ -25,7 +25,10 @@
cc_library_static {
name: "neuralnetworks_utils_hal_service",
- defaults: ["neuralnetworks_utils_defaults"],
+ defaults: [
+ "neuralnetworks_use_latest_utils_hal_aidl",
+ "neuralnetworks_utils_defaults",
+ ],
srcs: ["src/*"],
local_include_dirs: ["include/nnapi/hal"],
export_include_dirs: ["include"],
@@ -35,11 +38,9 @@
"neuralnetworks_utils_hal_1_1",
"neuralnetworks_utils_hal_1_2",
"neuralnetworks_utils_hal_1_3",
- "neuralnetworks_utils_hal_aidl",
"neuralnetworks_utils_hal_common",
],
shared_libs: [
- "android.hardware.neuralnetworks-V2-ndk",
"android.hardware.neuralnetworks@1.0",
"android.hardware.neuralnetworks@1.1",
"android.hardware.neuralnetworks@1.2",
diff --git a/power/1.0/vts/functional/OWNERS b/power/1.0/vts/OWNERS
similarity index 100%
copy from power/1.0/vts/functional/OWNERS
copy to power/1.0/vts/OWNERS
diff --git a/power/1.1/vts/OWNERS b/power/1.1/vts/OWNERS
new file mode 100644
index 0000000..3a64da7
--- /dev/null
+++ b/power/1.1/vts/OWNERS
@@ -0,0 +1,2 @@
+# Bug component: 158088
+wvw@google.com
diff --git a/power/1.0/vts/functional/OWNERS b/power/1.1/vts/functional/OWNERS
similarity index 100%
rename from power/1.0/vts/functional/OWNERS
rename to power/1.1/vts/functional/OWNERS
diff --git a/power/1.2/vts/OWNERS b/power/1.2/vts/OWNERS
new file mode 100644
index 0000000..4d8c7e9
--- /dev/null
+++ b/power/1.2/vts/OWNERS
@@ -0,0 +1,2 @@
+# Bug component: 158088
+include ../../1.1/vts/OWNERS
diff --git a/power/1.3/vts/OWNERS b/power/1.3/vts/OWNERS
new file mode 100644
index 0000000..4d8c7e9
--- /dev/null
+++ b/power/1.3/vts/OWNERS
@@ -0,0 +1,2 @@
+# Bug component: 158088
+include ../../1.1/vts/OWNERS