/* * Copyright (C) 2015 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 "VehicleNetwork" #include <utils/Log.h> #include <IVehicleNetwork.h> #include "VehicleNetworkProtoUtil.h" namespace android { static status_t copyString(const std::string& in, uint8_t** out, int32_t* len) { *len = in.length(); if (*len == 0) { *out = NULL; return NO_ERROR; } *out = new uint8_t[*len]; ASSERT_OR_HANDLE_NO_MEMORY(*out, return NO_MEMORY); memcpy(*out, in.data(), *len); return NO_ERROR; } status_t VehicleNetworkProtoUtil::toVehiclePropValue(const vehicle_prop_value_t& in, VehiclePropValue& out, bool /*inPlace*/) { out.set_prop(in.prop); out.set_value_type(in.value_type); out.set_timestamp(in.timestamp); out.set_zone(in.zone); switch (in.value_type) { case VEHICLE_VALUE_TYPE_STRING: { //TODO fix ugly copy here for inplace mode if (in.value.str_value.len > 0) { out.set_string_value((char*)in.value.str_value.data, in.value.str_value.len); } } break; case VEHICLE_VALUE_TYPE_BYTES: { if (in.value.bytes_value.len > 0) { out.set_bytes_value(in.value.bytes_value.data, in.value.bytes_value.len); } } break; case VEHICLE_VALUE_TYPE_FLOAT: case VEHICLE_VALUE_TYPE_FLOAT_VEC2: case VEHICLE_VALUE_TYPE_FLOAT_VEC3: case VEHICLE_VALUE_TYPE_FLOAT_VEC4: { int expectedSize = in.value_type - VEHICLE_VALUE_TYPE_FLOAT + 1; for (int i = 0; i < expectedSize; i++) { out.add_float_values(in.value.float_array[i]); } } break; case VEHICLE_VALUE_TYPE_INT64: { out.set_int64_value(in.value.int64_value); } break; case VEHICLE_VALUE_TYPE_BOOLEAN: case VEHICLE_VALUE_TYPE_ZONED_BOOLEAN: { out.add_int32_values(in.value.int32_value); } break; case VEHICLE_VALUE_TYPE_INT32: case VEHICLE_VALUE_TYPE_INT32_VEC2: case VEHICLE_VALUE_TYPE_INT32_VEC3: case VEHICLE_VALUE_TYPE_INT32_VEC4: { int expectedSize = in.value_type - VEHICLE_VALUE_TYPE_INT32 + 1; for (int i = 0; i < expectedSize; i++) { out.add_int32_values(in.value.int32_array[i]); } } break; case VEHICLE_VALUE_TYPE_ZONED_INT32: case VEHICLE_VALUE_TYPE_ZONED_INT32_VEC2: case VEHICLE_VALUE_TYPE_ZONED_INT32_VEC3: case VEHICLE_VALUE_TYPE_ZONED_INT32_VEC4: { int expectedSize = in.value_type - VEHICLE_VALUE_TYPE_ZONED_INT32 + 1; for (int i = 0; i < expectedSize; i++) { out.add_int32_values(in.value.int32_array[i]); } } break; case VEHICLE_VALUE_TYPE_ZONED_FLOAT: case VEHICLE_VALUE_TYPE_ZONED_FLOAT_VEC2: case VEHICLE_VALUE_TYPE_ZONED_FLOAT_VEC3: case VEHICLE_VALUE_TYPE_ZONED_FLOAT_VEC4: { int expectedSize = in.value_type - VEHICLE_VALUE_TYPE_ZONED_FLOAT + 1; for (int i = 0; i < expectedSize; i++) { out.add_float_values(in.value.float_array[i]); } } break; } return NO_ERROR; } status_t VehicleNetworkProtoUtil::fromVehiclePropValue(const VehiclePropValue& in, vehicle_prop_value_t& out, bool /*inPlace*/, bool canIgnoreNoData) { out.prop = in.prop(); out.value_type = in.value_type(); out.timestamp = in.timestamp(); out.zone = in.zone(); switch (out.value_type) { case VEHICLE_VALUE_TYPE_STRING: { if (!in.has_string_value()) { // set to NULL so that client can just delete this safely. out.value.str_value.data = NULL; out.value.str_value.len = 0; if (canIgnoreNoData) { return NO_ERROR; } else { ALOGE("fromVehiclePropValue, no string data"); return BAD_VALUE; } } //TODO fix copy... status_t r = copyString(in.string_value(), &(out.value.str_value.data), &(out.value.str_value.len)); if (r != NO_ERROR) { out.value.str_value.data = NULL; out.value.str_value.len = 0; ALOGE("copyString for string failed %d", r); return r; } } break; case VEHICLE_VALUE_TYPE_BYTES: { if (!in.has_bytes_value()) { out.value.bytes_value.data = NULL; out.value.bytes_value.len = 0; if (canIgnoreNoData) { return NO_ERROR; } else { ALOGE("fromVehiclePropValue, no byte data"); return BAD_VALUE; } } status_t r = copyString(in.bytes_value(), &(out.value.bytes_value.data), &(out.value.bytes_value.len)); if (r != NO_ERROR) { out.value.bytes_value.data = NULL; out.value.bytes_value.len = 0; ALOGE("copyString for bytes failed %d", r); return r; } } break; case VEHICLE_VALUE_TYPE_FLOAT: case VEHICLE_VALUE_TYPE_FLOAT_VEC2: case VEHICLE_VALUE_TYPE_FLOAT_VEC3: case VEHICLE_VALUE_TYPE_FLOAT_VEC4: { int expectedSize = out.value_type - VEHICLE_VALUE_TYPE_FLOAT + 1; if (in.float_values_size() != expectedSize) { if (canIgnoreNoData) { return NO_ERROR; } ALOGE("float value, wrong size %d, expecting %d", in.float_values_size(), expectedSize); return BAD_VALUE; } for (int i = 0; i < expectedSize; i++) { out.value.float_array[i] = in.float_values(i); } } break; case VEHICLE_VALUE_TYPE_INT64: { if (!in.has_int64_value()) { if (canIgnoreNoData) { return NO_ERROR; } ALOGE("no int64 value"); return BAD_VALUE; } out.value.int64_value = in.int64_value(); } break; case VEHICLE_VALUE_TYPE_BOOLEAN: case VEHICLE_VALUE_TYPE_ZONED_BOOLEAN: { if (in.int32_values_size() != 1) { if (canIgnoreNoData) { return NO_ERROR; } ALOGE("no int32 value"); return BAD_VALUE; } out.value.int32_value = in.int32_values(0); } break; case VEHICLE_VALUE_TYPE_INT32: case VEHICLE_VALUE_TYPE_INT32_VEC2: case VEHICLE_VALUE_TYPE_INT32_VEC3: case VEHICLE_VALUE_TYPE_INT32_VEC4: { int expectedSize = out.value_type - VEHICLE_VALUE_TYPE_INT32 + 1; if (in.int32_values_size() != expectedSize) { if (canIgnoreNoData) { return NO_ERROR; } ALOGE("int32 value, wrong size %d, expecting %d", in.int32_values_size(), expectedSize); return BAD_VALUE; } for (int i = 0; i < expectedSize; i++) { out.value.int32_array[i] = in.int32_values(i); } } break; case VEHICLE_VALUE_TYPE_ZONED_INT32: case VEHICLE_VALUE_TYPE_ZONED_INT32_VEC2: case VEHICLE_VALUE_TYPE_ZONED_INT32_VEC3: case VEHICLE_VALUE_TYPE_ZONED_INT32_VEC4: { int expectedSize = out.value_type - VEHICLE_VALUE_TYPE_ZONED_INT32 + 1; if (in.int32_values_size() != expectedSize) { if (canIgnoreNoData) { return NO_ERROR; } ALOGE("int32 value, wrong size %d, expecting %d", in.int32_values_size(), expectedSize); return BAD_VALUE; } for (int i = 0; i < expectedSize; i++) { out.value.int32_array[i] = in.int32_values(i); } } break; case VEHICLE_VALUE_TYPE_ZONED_FLOAT: case VEHICLE_VALUE_TYPE_ZONED_FLOAT_VEC2: case VEHICLE_VALUE_TYPE_ZONED_FLOAT_VEC3: case VEHICLE_VALUE_TYPE_ZONED_FLOAT_VEC4:{ int expectedSize = out.value_type - VEHICLE_VALUE_TYPE_ZONED_FLOAT + 1; if (in.float_values_size() != expectedSize) { if (canIgnoreNoData) { return NO_ERROR; } ALOGE("float value, wrong size %d, expecting %d", in.float_values_size(), expectedSize); return BAD_VALUE; } for (int i = 0; i < expectedSize; i++) { out.value.float_array[i] = in.float_values(i); } } break; default: { if (canIgnoreNoData) { return NO_ERROR; } ALOGE("fromVehiclePropValue unknown type 0x%x", out.value_type); return BAD_VALUE; } } return NO_ERROR; } status_t VehicleNetworkProtoUtil::toVehiclePropValues(const List<vehicle_prop_value_t*>& in, VehiclePropValues& out) { status_t r; for (auto& v : in) { VehiclePropValue* value = out.add_values(); r = toVehiclePropValue(*v, *value); if (r != NO_ERROR) { out.clear_values(); return r; } } return NO_ERROR; } status_t VehicleNetworkProtoUtil::fromVehiclePropValues(const VehiclePropValues& in, List<vehicle_prop_value_t*>& out) { status_t r; for (int i = 0; i < in.values_size(); i++) { vehicle_prop_value_t* v = new vehicle_prop_value_t(); memset(v, 0, sizeof(vehicle_prop_value_t)); ASSERT_OR_HANDLE_NO_MEMORY(v, r = NO_MEMORY;goto error); r = fromVehiclePropValue(in.values(i), *v); if (r != NO_ERROR) { delete v; goto error; } out.push_back(v); } return NO_ERROR; error: // clean up everything in List for (auto pv : out) { VehiclePropValueUtil::deleteMembers(pv); } return r; } status_t VehicleNetworkProtoUtil::toVehiclePropConfig(const vehicle_prop_config_t& in, VehiclePropConfig& out) { out.set_prop(in.prop); out.set_access(in.access); out.set_change_mode(in.change_mode); out.set_value_type(in.value_type); out.set_permission_model(in.permission_model); out.set_zones(in.vehicle_zone_flags); for (unsigned int i = 0; i < sizeof(in.config_array) / sizeof(int32_t); i++) { out.add_config_array(in.config_array[i]); } if (in.config_string.data != NULL && in.config_string.len != 0) { out.set_config_string((char*)in.config_string.data, in.config_string.len); } else { out.clear_config_string(); } switch (in.value_type) { case VEHICLE_VALUE_TYPE_FLOAT: case VEHICLE_VALUE_TYPE_FLOAT_VEC2: case VEHICLE_VALUE_TYPE_FLOAT_VEC3: case VEHICLE_VALUE_TYPE_FLOAT_VEC4: { out.add_float_maxs(in.float_max_value); out.add_float_mins(in.float_min_value); } break; case VEHICLE_VALUE_TYPE_ZONED_FLOAT: case VEHICLE_VALUE_TYPE_ZONED_FLOAT_VEC2: case VEHICLE_VALUE_TYPE_ZONED_FLOAT_VEC3: case VEHICLE_VALUE_TYPE_ZONED_FLOAT_VEC4: { int numZones = VehicleNetworkUtil::countNumberOfZones(in.vehicle_zone_flags); if (in.float_min_values == NULL) { if (in.float_max_values == NULL) { // all the same min/max for (int i = 0; i < numZones; i++) { out.add_float_maxs(in.float_max_value); out.add_float_mins(in.float_min_value); } } else { // invalid combination ALOGW("Zoned property 0x%x, min_values NULL while max_values not NULL", in.prop); return BAD_VALUE; } } else { if (in.float_max_values != NULL) { for (int i = 0; i < numZones; i++) { out.add_float_maxs(in.float_max_values[i]); out.add_float_mins(in.float_min_values[i]); } } else { // invalid combination ALOGW("Zoned property 0x%x, max_values NULL while min_values not NULL", in.prop); return BAD_VALUE; } } } break; case VEHICLE_VALUE_TYPE_INT64: { out.add_int64_maxs(in.int64_max_value); out.add_int64_mins(in.int64_min_value); } break; case VEHICLE_VALUE_TYPE_INT32: case VEHICLE_VALUE_TYPE_INT32_VEC2: case VEHICLE_VALUE_TYPE_INT32_VEC3: case VEHICLE_VALUE_TYPE_INT32_VEC4: { out.add_int32_maxs(in.int32_max_value); out.add_int32_mins(in.int32_min_value); } break; case VEHICLE_VALUE_TYPE_ZONED_INT32: case VEHICLE_VALUE_TYPE_ZONED_INT32_VEC2: case VEHICLE_VALUE_TYPE_ZONED_INT32_VEC3: case VEHICLE_VALUE_TYPE_ZONED_INT32_VEC4: { int numZones = VehicleNetworkUtil::countNumberOfZones(in.vehicle_zone_flags); if (in.int32_min_values == NULL) { if (in.int32_max_values == NULL) { // all the same min/max for (int i = 0; i < numZones; i++) { out.add_int32_maxs(in.int32_max_value); out.add_int32_mins(in.int32_min_value); } } else { // invalid combination ALOGW("Zoned property 0x%x, min_values NULL while max_values not NULL", in.prop); return BAD_VALUE; } } else { if (in.int32_max_values != NULL) { for (int i = 0; i < numZones; i++) { out.add_int32_maxs(in.int32_max_values[i]); out.add_int32_mins(in.int32_min_values[i]); } } else { // invalid combination ALOGW("Zoned property 0x%x, max_values NULL while min_values not NULL", in.prop); return BAD_VALUE; } } } break; } out.set_sample_rate_max(in.max_sample_rate); out.set_sample_rate_min(in.min_sample_rate); return NO_ERROR; } status_t VehicleNetworkProtoUtil::fromVehiclePropConfig(const VehiclePropConfig& in, vehicle_prop_config_t& out) { out.prop = in.prop(); out.access = in.access(); out.change_mode = in.change_mode(); out.value_type = in.value_type(); out.permission_model = in.permission_model(); out.vehicle_zone_flags = in.zones(); int maxConfigSize = sizeof(out.config_array) / sizeof(int32_t); int configSize = in.config_array_size(); if (configSize > maxConfigSize) { return BAD_VALUE; } int i = 0; for (; i < configSize; i++) { out.config_array[i] = in.config_array(i); } for (; i < maxConfigSize; i++) { out.config_array[i] = 0; } if (in.has_config_string()) { status_t r = copyString(in.config_string(), &(out.config_string.data), &(out.config_string.len)); if (r != NO_ERROR) { return r; } } else { out.config_string.data = NULL; out.config_string.len = 0; } switch (out.value_type) { case VEHICLE_VALUE_TYPE_FLOAT: case VEHICLE_VALUE_TYPE_FLOAT_VEC2: case VEHICLE_VALUE_TYPE_FLOAT_VEC3: case VEHICLE_VALUE_TYPE_FLOAT_VEC4: { if ((in.float_maxs_size() == 1) && (in.float_mins_size() == 1)) { out.float_max_value = in.float_maxs(0); out.float_min_value = in.float_mins(0); } else { ALOGW("no float max/min for property 0x%x", out.prop); out.float_max_value = 0; out.float_min_value = 0; } } break; case VEHICLE_VALUE_TYPE_ZONED_FLOAT: case VEHICLE_VALUE_TYPE_ZONED_FLOAT_VEC2: case VEHICLE_VALUE_TYPE_ZONED_FLOAT_VEC3: case VEHICLE_VALUE_TYPE_ZONED_FLOAT_VEC4: { int numZones = VehicleNetworkUtil::countNumberOfZones(out.vehicle_zone_flags); int maxSize = in.float_maxs_size(); int minSize = in.float_mins_size(); if (maxSize != minSize) { ALOGW("Zoned property 0x%x, config maxSize %d minSize %d", out.prop, maxSize, minSize); return BAD_VALUE; } if (maxSize == 0) { out.float_max_value = 0; out.float_min_value = 0; out.float_max_values = NULL; out.float_min_values = NULL; } else if (maxSize == 1) { // one for all out.float_max_value = in.float_maxs(0); out.float_min_value = in.float_mins(0); out.float_max_values = NULL; out.float_min_values = NULL; } else if (numZones > 1){ if (numZones != maxSize) { ALOGW("Zoned property 0x%x, config maxSize %d num Zones %d", out.prop, maxSize, numZones); return BAD_VALUE; } out.float_max_values = new float[numZones]; ASSERT_OR_HANDLE_NO_MEMORY(out.float_max_values, return NO_MEMORY); out.float_min_values = new float[numZones]; ASSERT_OR_HANDLE_NO_MEMORY(out.float_min_values, return NO_MEMORY); for (int i = 0; i < numZones; i++) { out.float_max_values[i] = in.float_maxs(i); out.float_min_values[i] = in.float_mins(i); } } } break; case VEHICLE_VALUE_TYPE_INT64: { if ((in.int64_maxs_size() == 1) && (in.int64_mins_size() == 1)) { out.int64_max_value = in.int64_maxs(0); out.int64_min_value = in.int64_mins(0); } else { ALOGW("no int64 max/min for property 0x%x", out.prop); out.int64_max_value = 0; out.int64_min_value = 0; } } break; case VEHICLE_VALUE_TYPE_INT32: case VEHICLE_VALUE_TYPE_INT32_VEC2: case VEHICLE_VALUE_TYPE_INT32_VEC3: case VEHICLE_VALUE_TYPE_INT32_VEC4: { if ((in.int32_maxs_size() == 1) && (in.int32_mins_size() == 1)) { out.int32_max_value = in.int32_maxs(0); out.int32_min_value = in.int32_mins(0); } else { ALOGW("no int32 max/min for property 0x%x", out.prop); out.int32_max_value = 0; out.int32_min_value = 0; } } break; case VEHICLE_VALUE_TYPE_ZONED_INT32: case VEHICLE_VALUE_TYPE_ZONED_INT32_VEC2: case VEHICLE_VALUE_TYPE_ZONED_INT32_VEC3: case VEHICLE_VALUE_TYPE_ZONED_INT32_VEC4: { int numZones = VehicleNetworkUtil::countNumberOfZones(out.vehicle_zone_flags); int maxSize = in.int32_maxs_size(); int minSize = in.int32_mins_size(); if (maxSize != minSize) { ALOGW("Zoned property 0x%x, config maxSize %d minSize %d", out.prop, maxSize, minSize); return BAD_VALUE; } if (maxSize == 0) { out.int32_max_value = 0; out.int32_min_value = 0; out.int32_max_values = NULL; out.int32_min_values = NULL; } else if (maxSize == 1) { // one for all out.int32_max_value = in.int32_maxs(0); out.int32_min_value = in.int32_mins(0); out.int32_max_values = NULL; out.int32_min_values = NULL; } else if (numZones > 1){ if (numZones != maxSize) { ALOGW("Zoned property 0x%x, config maxSize %d num Zones %d", out.prop, maxSize, numZones); return BAD_VALUE; } out.int32_max_values = new int32_t[numZones]; ASSERT_OR_HANDLE_NO_MEMORY(out.int32_max_values, return NO_MEMORY); out.int32_min_values = new int32_t[numZones]; ASSERT_OR_HANDLE_NO_MEMORY(out.int32_min_values, return NO_MEMORY); for (int i = 0; i < numZones; i++) { out.int32_max_values[i] = in.int32_maxs(i); out.int32_min_values[i] = in.int32_mins(i); } } } break; } out.max_sample_rate = in.sample_rate_max(); out.min_sample_rate = in.sample_rate_min(); return NO_ERROR; } status_t VehicleNetworkProtoUtil::toVehiclePropConfigs(List<vehicle_prop_config_t const*> &in, VehiclePropConfigs& out) { status_t r; for (auto& inEntry : in) { VehiclePropConfig* config = out.add_configs(); r = toVehiclePropConfig(*inEntry, *config); if (r != NO_ERROR) { out.clear_configs(); return r; } } return NO_ERROR; } status_t VehicleNetworkProtoUtil::fromVehiclePropConfigs(const VehiclePropConfigs& in, List<vehicle_prop_config_t const*>& out) { int32_t n = in.configs_size(); status_t r; for (int32_t i = 0; i < n; i++) { vehicle_prop_config_t* entry = new vehicle_prop_config_t(); ASSERT_OR_HANDLE_NO_MEMORY(entry, r = NO_MEMORY; goto error); memset(entry, 0, sizeof(vehicle_prop_config_t)); r = fromVehiclePropConfig(in.configs(i), *entry); if (r != NO_ERROR) { goto error; } out.push_back(entry); } return NO_ERROR; error: for (auto& e : out) { vehicle_prop_config_t* eDelete = const_cast<vehicle_prop_config_t*>(e); VehiclePropertiesUtil::deleteMembers(eDelete); delete eDelete; } out.clear(); return r; } status_t VehiclePropValueBinderUtil::writeToParcel(Parcel& parcel, const vehicle_prop_value_t& value) { parcel.writeInt32(1); // 0 means no value. For compatibility with aidl based code. std::unique_ptr<VehiclePropValue> v(new VehiclePropValue()); ASSERT_OR_HANDLE_NO_MEMORY(v.get(), return NO_MEMORY); VehicleNetworkProtoUtil::toVehiclePropValue(value, *v.get()); int size = v->ByteSize(); WritableBlobHolder blob(new Parcel::WritableBlob()); ASSERT_OR_HANDLE_NO_MEMORY(blob.blob, return NO_MEMORY); parcel.writeInt32(size); parcel.writeBlob(size, false, blob.blob); v->SerializeToArray(blob.blob->data(), size); return NO_ERROR; } status_t VehiclePropValueBinderUtil::readFromParcel(const Parcel& parcel, vehicle_prop_value_t* value, bool deleteMembers, bool canIgnoreNoData) { if (parcel.readInt32() == 0) { // no result ALOGE("readFromParcel, null data"); return BAD_VALUE; } ReadableBlobHolder blob(new Parcel::ReadableBlob()); ASSERT_OR_HANDLE_NO_MEMORY(blob.blob, return NO_MEMORY); int32_t size = parcel.readInt32(); status_t status = parcel.readBlob(size, blob.blob); if (status != NO_ERROR) { ALOGE("readFromParcel, cannot read blob"); return status; } std::unique_ptr<VehiclePropValue> v(new VehiclePropValue()); ASSERT_OR_HANDLE_NO_MEMORY(v.get(), return NO_MEMORY); if (!v->ParseFromArray(blob.blob->data(), size)) { ALOGE("readFromParcel, cannot parse"); return BAD_VALUE; } if (deleteMembers) { VehiclePropValueUtil::deleteMembers(value); } return VehicleNetworkProtoUtil::fromVehiclePropValue(*v.get(), *value, false /*inPlace*/, canIgnoreNoData); } }; //namespace android