Implemented IVehicle::debug() on default Vehicle HAL implementation.

This function allows developer to drive (no pun intend) the VHAL behaviour
using lshal, which is useful when implementing new CarService functionalities
that depend on VHAL properties.

Examples:

$ adb shell lshal debug android.hardware.automotive.vehicle@2.0::IVehicle/default --help
Usage:

[no args]: dumps (id and value) all supported properties
--help: shows this help
--list: lists the ids of all supported properties
--get <PROP1> [PROP2] [PROPN]: dumps the value of specific properties
--set <PROP> <i|s> <VALUE>: sets the value of property PROP, using an arbitrary number
  of key/value parameters (i for int32, s for string)

$ adb shell lshal debug android.hardware.automotive.vehicle@2.0::IVehicle/default | head -2
dumping 87 properties
1: {.timestamp = 0, .areaId = 0, .prop = 299896583, .status = AVAILABLE,
.value = {.int32Values = [3]{42, 1, 10}, .floatValues = [0]{}, .int64Values = [0]{},
.bytes = [0]{}, .stringValue = ""}}

$ adb shell lshal debug android.hardware.automotive.vehicle@2.0::IVehicle/default \
  --set 299896583 s Numbers i 4 i 8 i 15 i 16 i 23 i 42
Set property {.timestamp = 0, .areaId = 0, .prop = 299896583, .status = AVAILABLE,
.value = {.int32Values = [6]{4, 8, 15, 16, 23, 42}, .floatValues = [0]{},
.int64Values = [0]{}, .bytes = [0]{}, .stringValue = "Numbers"}}

$ adb shell lshal debug android.hardware.automotive.vehicle@2.0::IVehicle/default \
  --get 299896583

{.timestamp = 102372810363603, .areaId = 0, .prop = 299896583, .status = AVAILABLE,
.value = {.int32Values = [6]{4, 8, 15, 16, 23, 42}, .floatValues = [0]{},
.int64Values = [0]{}, .bytes = [0]{}, .stringValue = "Numbers"}}

Bug: 146207078
Test: see examples above
Test: atest android.hardware.automotive.vehicle@2.0-manager-unit-tests \
      android.hardware.automotive.vehicle@2.0-default-impl-unit-tests

Change-Id: I2a9bd6f39f2938653a404cab43bdae9e45476529
This commit is contained in:
Felipe Leme 2020-01-31 15:25:55 -08:00
parent 2ca858a491
commit 75a27f8b37
3 changed files with 264 additions and 4 deletions

View file

@ -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",
],
}

View file

@ -73,7 +73,9 @@ public:
int32_t propId) override;
Return<void> debugDump(debugDump_cb _hidl_cb = nullptr) override;
private:
Return<void> debug(const hidl_handle& fd, const hidl_vec<hidl_string>& options) override;
private:
using VehiclePropValuePtr = VehicleHal::VehiclePropValuePtr;
// Returns true if needs to call again shortly.
using RetriableAction = std::function<bool()>;
@ -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<hidl_string>& 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<hidl_string>& 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<hidl_string>& options);
void cmdSetOneProperty(int fd, const hidl_vec<hidl_string>& options);
static bool isSubscribable(const VehiclePropConfig& config,
SubscribeFlags flags);
static bool isSampleRateFixed(VehiclePropertyChangeMode mode);

View file

@ -21,11 +21,18 @@
#include <cmath>
#include <fstream>
#include <android/log.h>
#include <android-base/parseint.h>
#include <android-base/strings.h>
#include <android/hardware/automotive/vehicle/2.0/BpHwVehicleCallback.h>
#include <android/log.h>
#include <hwbinder/IPCThreadState.h>
#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<void> VehicleHalManager::debugDump(IVehicle::debugDump_cb _hidl_cb) {
return Void();
}
Return<void> VehicleHalManager::debug(const hidl_handle& fd, const hidl_vec<hidl_string>& 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<hidl_string>& 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<hidl_string>& 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 <PROP1> [PROP2] [PROPN]: dumps the value of specific properties \n");
// TODO: support other formats (int64, float, bytes)
dprintf(fd,
"--set <PROP> <i|s> <VALUE_1> [<i|s> <VALUE_N>]: 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<hidl_string>& 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<hidl_string>& 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");