diff --git a/automotive/vehicle/2.0/default/Android.bp b/automotive/vehicle/2.0/default/Android.bp index 1690163d2a..6a254a513a 100644 --- a/automotive/vehicle/2.0/default/Android.bp +++ b/automotive/vehicle/2.0/default/Android.bp @@ -46,6 +46,7 @@ cc_library_static { "common/src/VehicleObjectPool.cpp", "common/src/VehiclePropertyStore.cpp", "common/src/VehicleUtils.cpp", + "common/src/VmsUtils.cpp", ], local_include_dirs: ["common/include/vhal_v2_0"], export_include_dirs: ["common/include"], @@ -93,6 +94,7 @@ cc_test { "tests/VehicleHalManager_test.cpp", "tests/VehicleObjectPool_test.cpp", "tests/VehiclePropConfigIndex_test.cpp", + "tests/VmsUtils_test.cpp", ], header_libs: ["libbase_headers"], } diff --git a/automotive/vehicle/2.0/default/common/include/vhal_v2_0/VmsUtils.h b/automotive/vehicle/2.0/default/common/include/vhal_v2_0/VmsUtils.h new file mode 100644 index 0000000000..9e32bb5a89 --- /dev/null +++ b/automotive/vehicle/2.0/default/common/include/vhal_v2_0/VmsUtils.h @@ -0,0 +1,156 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef android_hardware_automotive_vehicle_V2_0_VmsUtils_H_ +#define android_hardware_automotive_vehicle_V2_0_VmsUtils_H_ + +#include +#include + +#include + +namespace android { +namespace hardware { +namespace automotive { +namespace vehicle { +namespace V2_0 { +namespace vms { + +// VmsUtils are a set of abstractions for creating and parsing Vehicle Property +// updates to VehicleProperty::VEHICLE_MAP_SERVICE. The format for parsing a +// VehiclePropValue update with a VMS message is specified in the Vehicle HIDL. +// +// This interface is meant for use by HAL clients of VMS; corresponding +// functionality is also provided by VMS in the embedded car service. + +// A VmsLayer is comprised of a type, subtype, and version. +struct VmsLayer { + VmsLayer(int type, int subtype, int version) : type(type), subtype(subtype), version(version) {} + int type; + int subtype; + int version; +}; + +struct VmsLayerAndPublisher { + VmsLayer layer; + int publisher_id; +}; + +// A VmsAssociatedLayer is used by subscribers to specify which publisher IDs +// are acceptable for a given layer. +struct VmsAssociatedLayer { + VmsLayer layer; + std::vector publisher_ids; +}; + +// A VmsLayerOffering refers to a single layer that can be published, along with +// its dependencies. Dependencies can be empty. +struct VmsLayerOffering { + VmsLayerOffering(VmsLayer layer, std::vector dependencies) + : layer(layer), dependencies(dependencies) {} + VmsLayerOffering(VmsLayer layer) : layer(layer), dependencies() {} + VmsLayer layer; + std::vector dependencies; +}; + +// A VmsSubscriptionsState is delivered in response to a +// VmsMessageType.SUBSCRIPTIONS_REQUEST or on the first SUBSCRIBE or last +// UNSUBSCRIBE for a layer. It indicates which layers or associated_layers are +// currently being subscribed to in the system. +struct VmsSubscriptionsState { + int sequence_number; + std::vector layers; + std::vector associated_layers; +}; + +struct VmsAvailabilityState { + int sequence_number; + std::vector associated_layers; +}; + +// Creates a VehiclePropValue containing a message of type +// VmsMessageType.SUBSCRIBE, specifying to the VMS service +// which layer to subscribe to. +std::unique_ptr createSubscribeMessage(const VmsLayer& layer); + +// Creates a VehiclePropValue containing a message of type +// VmsMessageType.SUBSCRIBE_TO_PUBLISHER, specifying to the VMS service +// which layer and publisher_id to subscribe to. +std::unique_ptr createSubscribeToPublisherMessage( + const VmsLayerAndPublisher& layer); + +// Creates a VehiclePropValue containing a message of type +// VmsMessageType.UNSUBSCRIBE, specifying to the VMS service +// which layer to unsubscribe from. +std::unique_ptr createUnsubscribeMessage(const VmsLayer& layer); + +// Creates a VehiclePropValue containing a message of type +// VmsMessageType.UNSUBSCRIBE_TO_PUBLISHER, specifying to the VMS service +// which layer and publisher_id to unsubscribe from. +std::unique_ptr createUnsubscribeToPublisherMessage( + const VmsLayerAndPublisher& layer); + +// Creates a VehiclePropValue containing a message of type +// VmsMessageType.OFFERING, specifying to the VMS service which layers are being +// offered and their dependencies, if any. +std::unique_ptr createOfferingMessage( + const std::vector& offering); + +// Creates a VehiclePropValue containing a message of type +// VmsMessageType.AVAILABILITY_REQUEST. +std::unique_ptr createAvailabilityRequest(); + +// Creates a VehiclePropValue containing a message of type +// VmsMessageType.AVAILABILITY_REQUEST. +std::unique_ptr createSubscriptionsRequest(); + +// Creates a VehiclePropValue containing a message of type VmsMessageType.DATA. +// Returns a nullptr if the byte string in bytes is empty. +// +// For example, to build a VehiclePropMessage containing a proto, the caller +// should convert the proto to a byte string using the SerializeToString proto +// API, then use this inteface to build the VehicleProperty. +std::unique_ptr createDataMessage(const std::string& bytes); + +// Returns true if the VehiclePropValue pointed to by value contains a valid Vms +// message, i.e. the VehicleProperty, VehicleArea, and VmsMessageType are all +// valid. Note: If the VmsMessageType enum is extended, this function will +// return false for any new message types added. +bool isValidVmsMessage(const VehiclePropValue& value); + +// Returns the message type. Expects that the VehiclePropValue contains a valid +// Vms message, as verified by isValidVmsMessage. +VmsMessageType parseMessageType(const VehiclePropValue& value); + +// Constructs a string byte array from a message of type VmsMessageType.DATA. +// Returns an empty string if the message type doesn't match or if the +// VehiclePropValue does not contain a byte array. +// +// A proto message can then be constructed by passing the result of this +// function to ParseFromString. +std::string parseData(const VehiclePropValue& value); + +// TODO(aditin): Need to implement additional parsing functions per message +// type. + +} // namespace vms +} // namespace V2_0 +} // namespace vehicle +} // namespace automotive +} // namespace hardware +} // namespace android + +#endif // android_hardware_automotive_vehicle_V2_0_VmsUtils_H_ diff --git a/automotive/vehicle/2.0/default/common/src/VmsUtils.cpp b/automotive/vehicle/2.0/default/common/src/VmsUtils.cpp new file mode 100644 index 0000000000..abf425fdf8 --- /dev/null +++ b/automotive/vehicle/2.0/default/common/src/VmsUtils.cpp @@ -0,0 +1,162 @@ +/* + * Copyright (C) 2018 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 "VmsUtils.h" + +#include + +namespace android { +namespace hardware { +namespace automotive { +namespace vehicle { +namespace V2_0 { +namespace vms { + +static constexpr int kMessageIndex = toInt(VmsBaseMessageIntegerValuesIndex::MESSAGE_TYPE); +static constexpr int kMessageTypeSize = 1; +static constexpr int kLayerNumberSize = 1; +static constexpr int kLayerSize = 3; +static constexpr int kLayerAndPublisherSize = 4; + +// TODO(aditin): We should extend the VmsMessageType enum to include a first and +// last, which would prevent breakages in this API. However, for all of the +// functions in this module, we only need to guarantee that the message type is +// between SUBSCRIBE and DATA. +static constexpr int kFirstMessageType = toInt(VmsMessageType::SUBSCRIBE); +static constexpr int kLastMessageType = toInt(VmsMessageType::DATA); + +std::unique_ptr createBaseVmsMessage(size_t message_size) { + auto result = createVehiclePropValue(VehiclePropertyType::INT32, message_size); + result->prop = toInt(VehicleProperty::VEHICLE_MAP_SERVICE); + result->areaId = toInt(VehicleArea::GLOBAL); + return result; +} + +std::unique_ptr createSubscribeMessage(const VmsLayer& layer) { + auto result = createBaseVmsMessage(kMessageTypeSize + kLayerSize); + result->value.int32Values = hidl_vec{toInt(VmsMessageType::SUBSCRIBE), layer.type, + layer.subtype, layer.version}; + return result; +} + +std::unique_ptr createSubscribeToPublisherMessage( + const VmsLayerAndPublisher& layer_publisher) { + auto result = createBaseVmsMessage(kMessageTypeSize + kLayerAndPublisherSize); + result->value.int32Values = hidl_vec{ + toInt(VmsMessageType::SUBSCRIBE_TO_PUBLISHER), layer_publisher.layer.type, + layer_publisher.layer.subtype, layer_publisher.layer.version, layer_publisher.publisher_id}; + return result; +} + +std::unique_ptr createUnsubscribeMessage(const VmsLayer& layer) { + auto result = createBaseVmsMessage(kMessageTypeSize + kLayerSize); + result->value.int32Values = hidl_vec{toInt(VmsMessageType::UNSUBSCRIBE), layer.type, + layer.subtype, layer.version}; + return result; +} + +std::unique_ptr createUnsubscribeToPublisherMessage( + const VmsLayerAndPublisher& layer_publisher) { + auto result = createBaseVmsMessage(kMessageTypeSize + kLayerAndPublisherSize); + result->value.int32Values = hidl_vec{ + toInt(VmsMessageType::UNSUBSCRIBE_TO_PUBLISHER), layer_publisher.layer.type, + layer_publisher.layer.subtype, layer_publisher.layer.version, layer_publisher.publisher_id}; + return result; +} + +std::unique_ptr createOfferingMessage( + const std::vector& offering) { + int message_size = kMessageTypeSize + kLayerNumberSize; + for (const auto& offer : offering) { + message_size += kLayerNumberSize + (1 + offer.dependencies.size()) * kLayerSize; + } + auto result = createBaseVmsMessage(message_size); + + std::vector offers = {toInt(VmsMessageType::OFFERING), + static_cast(offering.size())}; + for (const auto& offer : offering) { + std::vector layer_vector = {offer.layer.type, offer.layer.subtype, + offer.layer.version, + static_cast(offer.dependencies.size())}; + for (const auto& dependency : offer.dependencies) { + std::vector dependency_layer = {dependency.type, dependency.subtype, + dependency.version}; + layer_vector.insert(layer_vector.end(), dependency_layer.begin(), + dependency_layer.end()); + } + offers.insert(offers.end(), layer_vector.begin(), layer_vector.end()); + } + result->value.int32Values = offers; + return result; +} + +std::unique_ptr createAvailabilityRequest() { + auto result = createBaseVmsMessage(kMessageTypeSize); + result->value.int32Values = hidl_vec{ + toInt(VmsMessageType::AVAILABILITY_REQUEST), + }; + return result; +} + +std::unique_ptr createSubscriptionsRequest() { + auto result = createBaseVmsMessage(kMessageTypeSize); + result->value.int32Values = hidl_vec{ + toInt(VmsMessageType::SUBSCRIPTIONS_REQUEST), + }; + return result; +} + +std::unique_ptr createDataMessage(const std::string& bytes) { + auto result = createBaseVmsMessage(kMessageTypeSize); + result->value.int32Values = hidl_vec{toInt(VmsMessageType::DATA)}; + result->value.bytes = std::vector(bytes.begin(), bytes.end()); + return result; +} + +bool verifyPropertyAndArea(const VehiclePropValue& value) { + return (value.prop == toInt(VehicleProperty::VEHICLE_MAP_SERVICE) && + value.areaId == toInt(VehicleArea::GLOBAL)); +} + +bool verifyMessageType(const VehiclePropValue& value) { + return (value.value.int32Values.size() > 0 && + value.value.int32Values[kMessageIndex] >= kFirstMessageType && + value.value.int32Values[kMessageIndex] <= kLastMessageType); +} + +bool isValidVmsMessage(const VehiclePropValue& value) { + return (verifyPropertyAndArea(value) && verifyMessageType(value)); +} + +VmsMessageType parseMessageType(const VehiclePropValue& value) { + return static_cast(value.value.int32Values[kMessageIndex]); +} + +std::string parseData(const VehiclePropValue& value) { + if (isValidVmsMessage(value) && parseMessageType(value) == VmsMessageType::DATA && + value.value.bytes.size() > 0) { + return std::string(value.value.bytes.begin(), value.value.bytes.end()); + } else { + return std::string(); + } +} + +} // namespace vms +} // namespace V2_0 +} // namespace vehicle +} // namespace automotive +} // namespace hardware +} // namespace android diff --git a/automotive/vehicle/2.0/default/tests/VmsUtils_test.cpp b/automotive/vehicle/2.0/default/tests/VmsUtils_test.cpp new file mode 100644 index 0000000000..c102ce80fe --- /dev/null +++ b/automotive/vehicle/2.0/default/tests/VmsUtils_test.cpp @@ -0,0 +1,183 @@ +/* + * Copyright (C) 2018 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 +#include + +#include "VehicleHalTestUtils.h" +#include "vhal_v2_0/VmsUtils.h" + +namespace android { +namespace hardware { +namespace automotive { +namespace vehicle { +namespace V2_0 { +namespace vms { + +namespace { + +TEST(VmsUtilsTest, subscribeMessage) { + VmsLayer layer(1, 0, 2); + auto message = createSubscribeMessage(layer); + ASSERT_NE(message, nullptr); + EXPECT_TRUE(isValidVmsMessage(*message)); + EXPECT_EQ(message->prop, toInt(VehicleProperty::VEHICLE_MAP_SERVICE)); + EXPECT_EQ(message->areaId, toInt(VehicleArea::GLOBAL)); + EXPECT_EQ(message->value.int32Values.size(), 0x4ul); + EXPECT_EQ(parseMessageType(*message), VmsMessageType::SUBSCRIBE); + + // Layer + EXPECT_EQ(message->value.int32Values[1], 1); + EXPECT_EQ(message->value.int32Values[2], 0); + EXPECT_EQ(message->value.int32Values[3], 2); +} + +TEST(VmsUtilsTest, unsubscribeMessage) { + VmsLayer layer(1, 0, 2); + auto message = createUnsubscribeMessage(layer); + ASSERT_NE(message, nullptr); + EXPECT_TRUE(isValidVmsMessage(*message)); + EXPECT_EQ(message->prop, toInt(VehicleProperty::VEHICLE_MAP_SERVICE)); + EXPECT_EQ(message->areaId, toInt(VehicleArea::GLOBAL)); + EXPECT_EQ(message->value.int32Values.size(), 0x4ul); + EXPECT_EQ(parseMessageType(*message), VmsMessageType::UNSUBSCRIBE); + + // Layer + EXPECT_EQ(message->value.int32Values[1], 1); + EXPECT_EQ(message->value.int32Values[2], 0); + EXPECT_EQ(message->value.int32Values[3], 2); +} + +TEST(VmsUtilsTest, singleOfferingMessage) { + std::vector offering = {VmsLayerOffering(VmsLayer(1, 0, 2))}; + auto message = createOfferingMessage(offering); + ASSERT_NE(message, nullptr); + EXPECT_TRUE(isValidVmsMessage(*message)); + EXPECT_EQ(message->prop, toInt(VehicleProperty::VEHICLE_MAP_SERVICE)); + EXPECT_EQ(message->areaId, toInt(VehicleArea::GLOBAL)); + EXPECT_EQ(message->value.int32Values.size(), 0x6ul); + EXPECT_EQ(parseMessageType(*message), VmsMessageType::OFFERING); + + // Number of layer offerings + EXPECT_EQ(message->value.int32Values[1], 1); + + // Layer + EXPECT_EQ(message->value.int32Values[2], 1); + EXPECT_EQ(message->value.int32Values[3], 0); + EXPECT_EQ(message->value.int32Values[4], 2); + + // Number of dependencies + EXPECT_EQ(message->value.int32Values[5], 0); +} + +TEST(VmsUtilsTest, offeringWithDependencies) { + VmsLayer layer(1, 0, 2); + std::vector dependencies = {VmsLayer(2, 0, 2)}; + std::vector offering = {VmsLayerOffering(layer, dependencies)}; + auto message = createOfferingMessage(offering); + ASSERT_NE(message, nullptr); + EXPECT_TRUE(isValidVmsMessage(*message)); + EXPECT_EQ(message->prop, toInt(VehicleProperty::VEHICLE_MAP_SERVICE)); + EXPECT_EQ(message->areaId, toInt(VehicleArea::GLOBAL)); + EXPECT_EQ(message->value.int32Values.size(), 0x9ul); + EXPECT_EQ(parseMessageType(*message), VmsMessageType::OFFERING); + + // Number of layer offerings + EXPECT_EQ(message->value.int32Values[1], 1); + + // Layer + EXPECT_EQ(message->value.int32Values[2], 1); + EXPECT_EQ(message->value.int32Values[3], 0); + EXPECT_EQ(message->value.int32Values[4], 2); + + // Number of dependencies + EXPECT_EQ(message->value.int32Values[5], 1); + + // Dependency 1 + EXPECT_EQ(message->value.int32Values[6], 2); + EXPECT_EQ(message->value.int32Values[7], 0); + EXPECT_EQ(message->value.int32Values[8], 2); +} + +TEST(VmsUtilsTest, availabilityMessage) { + auto message = createAvailabilityRequest(); + ASSERT_NE(message, nullptr); + EXPECT_TRUE(isValidVmsMessage(*message)); + EXPECT_EQ(message->prop, toInt(VehicleProperty::VEHICLE_MAP_SERVICE)); + EXPECT_EQ(message->areaId, toInt(VehicleArea::GLOBAL)); + EXPECT_EQ(message->value.int32Values.size(), 0x1ul); + EXPECT_EQ(parseMessageType(*message), VmsMessageType::AVAILABILITY_REQUEST); +} + +TEST(VmsUtilsTest, subscriptionsMessage) { + auto message = createSubscriptionsRequest(); + ASSERT_NE(message, nullptr); + EXPECT_TRUE(isValidVmsMessage(*message)); + EXPECT_EQ(message->prop, toInt(VehicleProperty::VEHICLE_MAP_SERVICE)); + EXPECT_EQ(message->areaId, toInt(VehicleArea::GLOBAL)); + EXPECT_EQ(message->value.int32Values.size(), 0x1ul); + EXPECT_EQ(parseMessageType(*message), VmsMessageType::SUBSCRIPTIONS_REQUEST); +} + +TEST(VmsUtilsTest, dataMessage) { + std::string bytes = "aaa"; + auto message = createDataMessage(bytes); + ASSERT_NE(message, nullptr); + EXPECT_TRUE(isValidVmsMessage(*message)); + EXPECT_EQ(message->prop, toInt(VehicleProperty::VEHICLE_MAP_SERVICE)); + EXPECT_EQ(message->areaId, toInt(VehicleArea::GLOBAL)); + EXPECT_EQ(message->value.int32Values.size(), 0x1ul); + EXPECT_EQ(parseMessageType(*message), VmsMessageType::DATA); + EXPECT_EQ(message->value.bytes.size(), bytes.size()); + EXPECT_EQ(memcmp(message->value.bytes.data(), bytes.data(), bytes.size()), 0); +} + +TEST(VmsUtilsTest, emptyMessageInvalid) { + VehiclePropValue empty_prop; + EXPECT_FALSE(isValidVmsMessage(empty_prop)); +} + +TEST(VmsUtilsTest, invalidMessageType) { + VmsLayer layer(1, 0, 2); + auto message = createSubscribeMessage(layer); + message->value.int32Values[0] = 0; + + EXPECT_FALSE(isValidVmsMessage(*message)); +} + +TEST(VmsUtilsTest, parseDataMessage) { + std::string bytes = "aaa"; + auto message = createDataMessage(bytes); + auto data_str = parseData(*message); + ASSERT_FALSE(data_str.empty()); + EXPECT_EQ(data_str, bytes); +} + +TEST(VmsUtilsTest, parseInvalidDataMessage) { + VmsLayer layer(1, 0, 2); + auto message = createSubscribeMessage(layer); + auto data_str = parseData(*message); + EXPECT_TRUE(data_str.empty()); +} + +} // namespace + +} // namespace vms +} // namespace V2_0 +} // namespace vehicle +} // namespace automotive +} // namespace hardware +} // namespace android