diff --git a/automotive/vehicle/2.0/default/Android.bp b/automotive/vehicle/2.0/default/Android.bp index a94a37e0f1..e325889517 100644 --- a/automotive/vehicle/2.0/default/Android.bp +++ b/automotive/vehicle/2.0/default/Android.bp @@ -47,6 +47,9 @@ cc_library { "common/src/VehicleUtils.cpp", "common/src/VmsUtils.cpp", ], + shared_libs: [ + "libbase", + ], local_include_dirs: ["common/include/vhal_v2_0"], export_include_dirs: ["common/include"], } @@ -109,6 +112,9 @@ cc_test { "tests/VehiclePropConfigIndex_test.cpp", "tests/VmsUtils_test.cpp", ], + shared_libs: [ + "libbase", + ], header_libs: ["libbase_headers"], test_suites: ["general-tests"], } @@ -173,7 +179,7 @@ cc_binary { "libqemu_pipe", ], cflags: [ - "-Wno-unused-parameter" + "-Wno-unused-parameter", ], } @@ -200,6 +206,6 @@ cc_binary { "android.hardware.automotive.vehicle@2.0-virtualization-utils", ], cflags: [ - "-Wno-unused-parameter" + "-Wno-unused-parameter", ], } diff --git a/automotive/vehicle/2.0/default/common/include/vhal_v2_0/VehicleHalManager.h b/automotive/vehicle/2.0/default/common/include/vhal_v2_0/VehicleHalManager.h index c1e9e88609..fcfe7612ac 100644 --- a/automotive/vehicle/2.0/default/common/include/vhal_v2_0/VehicleHalManager.h +++ b/automotive/vehicle/2.0/default/common/include/vhal_v2_0/VehicleHalManager.h @@ -73,7 +73,9 @@ public: int32_t propId) override; Return debugDump(debugDump_cb _hidl_cb = nullptr) override; -private: + Return debug(const hidl_handle& fd, const hidl_vec& options) override; + + private: using VehiclePropValuePtr = VehicleHal::VehiclePropValuePtr; // Returns true if needs to call again shortly. using RetriableAction = std::function; @@ -96,6 +98,22 @@ private: bool checkReadPermission(const VehiclePropConfig &config) const; void onAllClientsUnsubscribed(int32_t propertyId); + // Dump and commands + // TODO: most functions below (exception dump() and cmdSetOne()) should be const, but they rely + // on IVehicle.get(), which isn't... + void cmdDump(int fd, const hidl_vec& options); + void cmdDumpOneProperty(int fd, int32_t prop, int32_t areaId); + void cmdDumpOneProperty(int fd, int rowNumber, const VehiclePropConfig& config); + + static bool checkArgumentsSize(int fd, const hidl_vec& options, size_t minSize); + static bool checkCallerHasWritePermissions(int fd); + static bool safelyParseInt(int fd, int index, std::string s, int* out); + void cmdHelp(int fd) const; + void cmdListAllProperties(int fd) const; + void cmdDumpAllProperties(int fd); + void cmdDumpSpecificProperties(int fd, const hidl_vec& options); + void cmdSetOneProperty(int fd, const hidl_vec& options); + static bool isSubscribable(const VehiclePropConfig& config, SubscribeFlags flags); static bool isSampleRateFixed(VehiclePropertyChangeMode mode); diff --git a/automotive/vehicle/2.0/default/common/src/VehicleHalManager.cpp b/automotive/vehicle/2.0/default/common/src/VehicleHalManager.cpp index 393d3ecc5e..4249a61107 100644 --- a/automotive/vehicle/2.0/default/common/src/VehicleHalManager.cpp +++ b/automotive/vehicle/2.0/default/common/src/VehicleHalManager.cpp @@ -21,11 +21,18 @@ #include #include -#include +#include +#include #include +#include + +#include #include "VehicleUtils.h" +// TODO: figure out how to include private/android_filesystem_config.h instead... +#define AID_ROOT 0 /* traditional unix root user */ + namespace android { namespace hardware { namespace automotive { @@ -34,6 +41,10 @@ namespace V2_0 { using namespace std::placeholders; +using ::android::base::EqualsIgnoreCase; +using ::android::hardware::hidl_handle; +using ::android::hardware::hidl_string; + constexpr std::chrono::milliseconds kHalEventBatchingTimeWindow(10); const VehiclePropValue kEmptyValue{}; @@ -172,6 +183,231 @@ Return VehicleHalManager::debugDump(IVehicle::debugDump_cb _hidl_cb) { return Void(); } +Return VehicleHalManager::debug(const hidl_handle& fd, const hidl_vec& options) { + if (fd.getNativeHandle() != nullptr && fd->numFds > 0) { + cmdDump(fd->data[0], options); + } else { + ALOGE("Invalid parameters passed to debug()"); + } + return Void(); +} + +void VehicleHalManager::cmdDump(int fd, const hidl_vec& options) { + if (options.size() == 0) { + cmdDumpAllProperties(fd); + return; + } + std::string option = options[0]; + if (EqualsIgnoreCase(option, "--help")) { + cmdHelp(fd); + } else if (EqualsIgnoreCase(option, "--list")) { + cmdListAllProperties(fd); + } else if (EqualsIgnoreCase(option, "--get")) { + cmdDumpSpecificProperties(fd, options); + } else if (EqualsIgnoreCase(option, "--set")) { + cmdSetOneProperty(fd, options); + } else { + dprintf(fd, "Invalid option: %s\n", option.c_str()); + } +} + +bool VehicleHalManager::checkCallerHasWritePermissions(int fd) { + // Double check that's only called by root - it should be be blocked at the HIDL debug() level, + // but it doesn't hurt to make sure... + if (hardware::IPCThreadState::self()->getCallingUid() != AID_ROOT) { + dprintf(fd, "Must be root\n"); + return false; + } + return true; +} + +bool VehicleHalManager::checkArgumentsSize(int fd, const hidl_vec& options, + size_t minSize) { + size_t size = options.size(); + if (size >= minSize) { + return true; + } + dprintf(fd, "Invalid number of arguments: required at least %zu, got %zu\n", minSize, size); + return false; +} + +bool VehicleHalManager::safelyParseInt(int fd, int index, std::string s, int* out) { + if (!android::base::ParseInt(s, out)) { + dprintf(fd, "non-integer argument at index %d: %s\n", index, s.c_str()); + return false; + } + return true; +} + +void VehicleHalManager::cmdHelp(int fd) const { + dprintf(fd, "Usage: \n\n"); + dprintf(fd, "[no args]: dumps (id and value) all supported properties \n"); + dprintf(fd, "--help: shows this help\n"); + dprintf(fd, "--list: lists the ids of all supported properties\n"); + dprintf(fd, "--get [PROP2] [PROPN]: dumps the value of specific properties \n"); + // TODO: support other formats (int64, float, bytes) + dprintf(fd, + "--set [ ]: sets the value of property PROP, using" + " arbitrary number of key/value parameters (i for int32, s for string). Notice that " + "the string value can be set just once, while the other can have multiple values " + "(so they're used in the respective array)\n"); +} + +void VehicleHalManager::cmdListAllProperties(int fd) const { + auto& halConfig = mConfigIndex->getAllConfigs(); + size_t size = halConfig.size(); + if (size == 0) { + dprintf(fd, "no properties to list\n"); + return; + } + int i = 0; + dprintf(fd, "listing %zu properties\n", size); + for (const auto& config : halConfig) { + dprintf(fd, "%d: %d\n", ++i, config.prop); + } +} + +void VehicleHalManager::cmdDumpAllProperties(int fd) { + auto& halConfig = mConfigIndex->getAllConfigs(); + size_t size = halConfig.size(); + if (size == 0) { + dprintf(fd, "no properties to dump\n"); + return; + } + int rowNumber = 0; + dprintf(fd, "dumping %zu properties\n", size); + for (auto& config : halConfig) { + cmdDumpOneProperty(fd, ++rowNumber, config); + } +} + +void VehicleHalManager::cmdDumpOneProperty(int fd, int rowNumber, const VehiclePropConfig& config) { + size_t numberAreas = config.areaConfigs.size(); + if (numberAreas == 0) { + if (rowNumber > 0) { + dprintf(fd, "%d: ", rowNumber); + } + cmdDumpOneProperty(fd, config.prop, /* areaId= */ 0); + return; + } + for (size_t j = 0; j < numberAreas; ++j) { + if (rowNumber > 0) { + if (numberAreas > 1) { + dprintf(fd, "%d/%zu: ", rowNumber, j); + } else { + dprintf(fd, "%d: ", rowNumber); + } + } + cmdDumpOneProperty(fd, config.prop, config.areaConfigs[j].areaId); + } +} + +void VehicleHalManager::cmdDumpSpecificProperties(int fd, const hidl_vec& options) { + if (!checkArgumentsSize(fd, options, 2)) return; + + // options[0] is the command itself... + int rowNumber = 0; + size_t size = options.size(); + for (size_t i = 1; i < size; ++i) { + int prop; + if (!safelyParseInt(fd, i, options[i], &prop)) return; + const auto* config = getPropConfigOrNull(prop); + if (config == nullptr) { + dprintf(fd, "No property %d\n", prop); + continue; + } + if (size > 2) { + // Only show row number if there's more than 1 + rowNumber++; + } + cmdDumpOneProperty(fd, rowNumber, *config); + } +} + +void VehicleHalManager::cmdDumpOneProperty(int fd, int32_t prop, int32_t areaId) { + VehiclePropValue input; + input.prop = prop; + input.areaId = areaId; + auto callback = [&](StatusCode status, const VehiclePropValue& output) { + if (status == StatusCode::OK) { + dprintf(fd, "%s\n", toString(output).c_str()); + } else { + dprintf(fd, "Could not get property %d. Error: %s\n", prop, toString(status).c_str()); + } + }; + get(input, callback); +} + +void VehicleHalManager::cmdSetOneProperty(int fd, const hidl_vec& options) { + if (!checkCallerHasWritePermissions(fd) || !checkArgumentsSize(fd, options, 3)) return; + + size_t size = options.size(); + + // Syntax is --set PROP Type1 Value1 TypeN ValueN, so number of arguments must be even + if (size % 2 != 0) { + dprintf(fd, "must pass even number of arguments (passed %zu)\n", size); + return; + } + int numberValues = (size - 2) / 2; + + VehiclePropValue prop; + if (!safelyParseInt(fd, 1, options[1], &prop.prop)) return; + prop.timestamp = 0; + prop.areaId = 0; // TODO: add option to pass areaId as parameter + prop.status = VehiclePropertyStatus::AVAILABLE; + + // First pass calculate sizes + int sizeInt32 = 0; + int stringIndex = 0; + for (int i = 2, kv = 1; kv <= numberValues; kv++) { + // iterate through the kv=1..n key/value pairs, accessing indexes i / i+1 at each step + std::string type = options[i]; + std::string value = options[i + 1]; + if (EqualsIgnoreCase(type, "i")) { + sizeInt32++; + } else if (EqualsIgnoreCase(type, "s")) { + if (stringIndex != 0) { + dprintf(fd, + "defining string value (%s) again at index %d (already defined at %d=%s" + ")\n", + value.c_str(), i, stringIndex, options[stringIndex + 1].c_str()); + return; + } + stringIndex = i; + } else { + dprintf(fd, "invalid (%s) type at index %d\n", type.c_str(), i); + return; + } + i += 2; + } + prop.value.int32Values.resize(sizeInt32); + + // Second pass: populate it + int indexInt32 = 0; + for (int i = 2, kv = 1; kv <= numberValues; kv++) { + // iterate through the kv=1..n key/value pairs, accessing indexes i / i+1 at each step + int valueIndex = i + 1; + std::string type = options[i]; + std::string value = options[valueIndex]; + if (EqualsIgnoreCase(type, "i")) { + int safeInt; + if (!safelyParseInt(fd, valueIndex, value, &safeInt)) return; + prop.value.int32Values[indexInt32++] = safeInt; + } else if (EqualsIgnoreCase(type, "s")) { + prop.value.stringValue = value; + } + i += 2; + } + ALOGD("Setting prop %s", toString(prop).c_str()); + auto status = set(prop); + if (status == StatusCode::OK) { + dprintf(fd, "Set property %s\n", toString(prop).c_str()); + } else { + dprintf(fd, "Failed to set property %s: %s\n", toString(prop).c_str(), + toString(status).c_str()); + } +} + void VehicleHalManager::init() { ALOGI("VehicleHalManager::init");