Support debug dump
Test: atest FakeVehicleHardwareTest
Bug: 199314530
Change-Id: Ied0d1cd7c21e62a7db085f3b5be465cb0d576e73
diff --git a/automotive/vehicle/aidl/impl/fake_impl/hardware/include/FakeVehicleHardware.h b/automotive/vehicle/aidl/impl/fake_impl/hardware/include/FakeVehicleHardware.h
index cab184b..e701078 100644
--- a/automotive/vehicle/aidl/impl/fake_impl/hardware/include/FakeVehicleHardware.h
+++ b/automotive/vehicle/aidl/impl/fake_impl/hardware/include/FakeVehicleHardware.h
@@ -23,7 +23,9 @@
#include <IVehicleHardware.h>
#include <VehicleHalTypes.h>
#include <VehiclePropertyStore.h>
+#include <android-base/parseint.h>
#include <android-base/result.h>
+#include <android-base/stringprintf.h>
#include <android-base/thread_annotations.h>
#include <map>
@@ -120,6 +122,28 @@
::android::base::Result<VehiclePropValuePool::RecyclableType> getUserHalProp(
const ::aidl::android::hardware::automotive::vehicle::VehiclePropValue& value) const;
bool isHvacPropAndHvacNotAvailable(int32_t propId);
+
+ std::string dumpAllProperties();
+ std::string dumpOnePropertyByConfig(
+ int rowNumber,
+ const ::aidl::android::hardware::automotive::vehicle::VehiclePropConfig& config);
+ std::string dumpOnePropertyById(int32_t propId, int32_t areaId);
+ std::string dumpHelp();
+ std::string dumpListProperties();
+ std::string dumpSpecificProperty(const std::vector<std::string>& options);
+
+ template <typename T>
+ ::android::base::Result<T> safelyParseInt(int index, const std::string& s) {
+ T out;
+ if (!::android::base::ParseInt(s, &out)) {
+ return ::android::base::Error() << ::android::base::StringPrintf(
+ "non-integer argument at index %d: %s\n", index, s.c_str());
+ }
+ return out;
+ }
+
+ ::android::base::Result<void> checkArgumentsSize(const std::vector<std::string>& options,
+ size_t minSize);
};
} // namespace fake
diff --git a/automotive/vehicle/aidl/impl/fake_impl/hardware/src/FakeVehicleHardware.cpp b/automotive/vehicle/aidl/impl/fake_impl/hardware/src/FakeVehicleHardware.cpp
index 9c97a2c..1f8e436 100644
--- a/automotive/vehicle/aidl/impl/fake_impl/hardware/src/FakeVehicleHardware.cpp
+++ b/automotive/vehicle/aidl/impl/fake_impl/hardware/src/FakeVehicleHardware.cpp
@@ -26,6 +26,7 @@
#include <VehicleHalTypes.h>
#include <VehicleUtils.h>
#include <android-base/properties.h>
+#include <android-base/strings.h>
#include <utils/Log.h>
#include <utils/SystemClock.h>
@@ -59,8 +60,10 @@
using ::aidl::android::hardware::automotive::vehicle::VehiclePropertyType;
using ::aidl::android::hardware::automotive::vehicle::VehiclePropValue;
+using ::android::base::EqualsIgnoreCase;
using ::android::base::Error;
using ::android::base::Result;
+using ::android::base::StringPrintf;
const char* VENDOR_OVERRIDE_DIR = "/vendor/etc/automotive/vhaloverride/";
const char* OVERRIDE_PROPERTY = "persist.vendor.vhal_init_value_override";
@@ -484,14 +487,148 @@
return StatusCode::OK;
}
-DumpResult FakeVehicleHardware::dump(const std::vector<std::string>&) {
+DumpResult FakeVehicleHardware::dump(const std::vector<std::string>& options) {
DumpResult result;
- // TODO(b/201830716): Implement this.
+ result.callerShouldDumpState = false;
+ if (options.size() == 0) {
+ // We only want caller to dump default state when there is no options.
+ result.callerShouldDumpState = true;
+ result.buffer = dumpAllProperties();
+ return result;
+ }
+ std::string option = options[0];
+ if (EqualsIgnoreCase(option, "--help")) {
+ result.buffer = dumpHelp();
+ return result;
+ } else if (EqualsIgnoreCase(option, "--list")) {
+ result.buffer = dumpListProperties();
+ } else if (EqualsIgnoreCase(option, "--get")) {
+ result.buffer = dumpSpecificProperty(options);
+ } else if (EqualsIgnoreCase(option, "--set")) {
+ // TODO(b/214613918): Support debug set values.
+ } else {
+ result.buffer = StringPrintf("Invalid option: %s\n", option.c_str());
+ }
return result;
}
+std::string FakeVehicleHardware::dumpHelp() {
+ return "Usage: \n\n"
+ "[no args]: dumps (id and value) all supported properties \n"
+ "--help: shows this help\n"
+ "--list: lists the ids of all supported properties\n"
+ "--get <PROP1> [PROP2] [PROPN]: dumps the value of specific properties \n"
+ "--set <PROP> [-i INT_VALUE [INT_VALUE ...]] [-i64 INT64_VALUE [INT64_VALUE ...]] "
+ "[-f FLOAT_VALUE [FLOAT_VALUE ...]] [-s STR_VALUE] "
+ "[-b BYTES_VALUE] [-a AREA_ID] : sets the value of property PROP. "
+ "Notice that the string, bytes and area value can be set just once, while the other can"
+ " have multiple values (so they're used in the respective array), "
+ "BYTES_VALUE is in the form of 0xXXXX, e.g. 0xdeadbeef.\n";
+}
+
+std::string FakeVehicleHardware::dumpAllProperties() {
+ auto configs = mServerSidePropStore->getAllConfigs();
+ if (configs.size() == 0) {
+ return "no properties to dump\n";
+ }
+ std::string msg = StringPrintf("dumping %zu properties\n", configs.size());
+ int rowNumber = 1;
+ for (const VehiclePropConfig& config : configs) {
+ msg += dumpOnePropertyByConfig(rowNumber++, config);
+ }
+ return msg;
+}
+
+std::string FakeVehicleHardware::dumpOnePropertyByConfig(int rowNumber,
+ const VehiclePropConfig& config) {
+ size_t numberAreas = config.areaConfigs.size();
+ std::string msg = "";
+ if (numberAreas == 0) {
+ msg += StringPrintf("%d: ", rowNumber);
+ msg += dumpOnePropertyById(config.prop, /* areaId= */ 0);
+ return msg;
+ }
+ for (size_t j = 0; j < numberAreas; ++j) {
+ if (numberAreas > 1) {
+ msg += StringPrintf("%d-%zu: ", rowNumber, j);
+ } else {
+ msg += StringPrintf("%d: ", rowNumber);
+ }
+ msg += dumpOnePropertyById(config.prop, config.areaConfigs[j].areaId);
+ }
+ return msg;
+}
+
+std::string FakeVehicleHardware::dumpOnePropertyById(int32_t propId, int32_t areaId) {
+ VehiclePropValue value = {
+ .prop = propId,
+ .areaId = areaId,
+ };
+ bool isSpecialValue = false;
+ auto result = maybeGetSpecialValue(value, &isSpecialValue);
+ if (!isSpecialValue) {
+ result = mServerSidePropStore->readValue(value);
+ }
+ if (!result.ok()) {
+ return StringPrintf("failed to read property value: %d, error: %s, code: %d\n", propId,
+ getErrorMsg(result).c_str(), getIntErrorCode(result));
+
+ } else {
+ return result.value()->toString() + "\n";
+ }
+}
+
+std::string FakeVehicleHardware::dumpListProperties() {
+ auto configs = mServerSidePropStore->getAllConfigs();
+ if (configs.size() == 0) {
+ return "no properties to list\n";
+ }
+ int rowNumber = 1;
+ std::string msg = StringPrintf("listing %zu properties\n", configs.size());
+ for (const auto& config : configs) {
+ msg += StringPrintf("%d: %d\n", rowNumber++, config.prop);
+ }
+ return msg;
+}
+
+Result<void> FakeVehicleHardware::checkArgumentsSize(const std::vector<std::string>& options,
+ size_t minSize) {
+ size_t size = options.size();
+ if (size >= minSize) {
+ return {};
+ }
+ return Error() << StringPrintf("Invalid number of arguments: required at least %zu, got %zu\n",
+ minSize, size);
+}
+
+std::string FakeVehicleHardware::dumpSpecificProperty(const std::vector<std::string>& options) {
+ if (auto result = checkArgumentsSize(options, /*minSize=*/2); !result.ok()) {
+ return getErrorMsg(result);
+ }
+
+ // options[0] is the command itself...
+ int rowNumber = 1;
+ size_t size = options.size();
+ std::string msg = "";
+ for (size_t i = 1; i < size; ++i) {
+ auto propResult = safelyParseInt<int32_t>(i, options[i]);
+ if (!propResult.ok()) {
+ msg += getErrorMsg(propResult);
+ continue;
+ }
+ int32_t prop = propResult.value();
+ auto result = mServerSidePropStore->getConfig(prop);
+ if (!result.ok()) {
+ msg += StringPrintf("No property %d\n", prop);
+ continue;
+ }
+ msg += dumpOnePropertyByConfig(rowNumber++, *result.value());
+ }
+ return msg;
+}
+
StatusCode FakeVehicleHardware::checkHealth() {
- // TODO(b/201830716): Implement this.
+ // Always return OK for checkHealth.
return StatusCode::OK;
}
diff --git a/automotive/vehicle/aidl/impl/fake_impl/hardware/test/FakeVehicleHardwareTest.cpp b/automotive/vehicle/aidl/impl/fake_impl/hardware/test/FakeVehicleHardwareTest.cpp
index 970d044..f821dfe 100644
--- a/automotive/vehicle/aidl/impl/fake_impl/hardware/test/FakeVehicleHardwareTest.cpp
+++ b/automotive/vehicle/aidl/impl/fake_impl/hardware/test/FakeVehicleHardwareTest.cpp
@@ -24,6 +24,7 @@
#include <android-base/expected.h>
#include <android-base/file.h>
+#include <android-base/stringprintf.h>
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include <utils/Log.h>
@@ -52,8 +53,10 @@
using ::aidl::android::hardware::automotive::vehicle::VehiclePropertyStatus;
using ::aidl::android::hardware::automotive::vehicle::VehiclePropValue;
using ::android::base::expected;
+using ::android::base::StringPrintf;
using ::android::base::unexpected;
using ::testing::ContainerEq;
+using ::testing::ContainsRegex;
using ::testing::Eq;
using ::testing::IsSubsetOf;
using ::testing::WhenSortedBy;
@@ -1203,6 +1206,82 @@
}));
}
+TEST_F(FakeVehicleHardwareTest, testDumpAllProperties) {
+ std::vector<std::string> options;
+ DumpResult result = getHardware()->dump(options);
+ ASSERT_TRUE(result.callerShouldDumpState);
+ ASSERT_NE(result.buffer, "");
+ ASSERT_THAT(result.buffer, ContainsRegex("dumping .+ properties"));
+}
+
+TEST_F(FakeVehicleHardwareTest, testDumpHelp) {
+ std::vector<std::string> options;
+ options.push_back("--help");
+ DumpResult result = getHardware()->dump(options);
+ ASSERT_FALSE(result.callerShouldDumpState);
+ ASSERT_NE(result.buffer, "");
+ ASSERT_THAT(result.buffer, ContainsRegex("Usage: "));
+}
+
+TEST_F(FakeVehicleHardwareTest, testDumpListProperties) {
+ std::vector<std::string> options;
+ options.push_back("--list");
+ DumpResult result = getHardware()->dump(options);
+ ASSERT_FALSE(result.callerShouldDumpState);
+ ASSERT_NE(result.buffer, "");
+ ASSERT_THAT(result.buffer, ContainsRegex("listing .+ properties"));
+}
+
+TEST_F(FakeVehicleHardwareTest, testDumpSpecificProperties) {
+ std::vector<std::string> options;
+ options.push_back("--get");
+ std::string prop1 = std::to_string(toInt(VehicleProperty::INFO_FUEL_CAPACITY));
+ std::string prop2 = std::to_string(toInt(VehicleProperty::TIRE_PRESSURE));
+ options.push_back(prop1);
+ options.push_back(prop2);
+ DumpResult result = getHardware()->dump(options);
+ ASSERT_FALSE(result.callerShouldDumpState);
+ ASSERT_NE(result.buffer, "");
+ ASSERT_THAT(result.buffer,
+ ContainsRegex(StringPrintf("1:.*prop: %s.*\n2-0:.*prop: %s.*\n2-1:.*prop: %s.*\n",
+ prop1.c_str(), prop2.c_str(), prop2.c_str())));
+}
+
+TEST_F(FakeVehicleHardwareTest, testDumpSpecificPropertiesInvalidProp) {
+ std::vector<std::string> options;
+ options.push_back("--get");
+ std::string prop1 = std::to_string(toInt(VehicleProperty::INFO_FUEL_CAPACITY));
+ std::string prop2 = std::to_string(INVALID_PROP_ID);
+ options.push_back(prop1);
+ options.push_back(prop2);
+ DumpResult result = getHardware()->dump(options);
+ ASSERT_FALSE(result.callerShouldDumpState);
+ ASSERT_NE(result.buffer, "");
+ ASSERT_THAT(result.buffer, ContainsRegex(StringPrintf("1:.*prop: %s.*\nNo property %d\n",
+ prop1.c_str(), INVALID_PROP_ID)));
+}
+
+TEST_F(FakeVehicleHardwareTest, testDumpSpecificPropertiesNoArg) {
+ std::vector<std::string> options;
+ options.push_back("--get");
+
+ // No arguments.
+ DumpResult result = getHardware()->dump(options);
+ ASSERT_FALSE(result.callerShouldDumpState);
+ ASSERT_NE(result.buffer, "");
+ ASSERT_THAT(result.buffer, ContainsRegex("Invalid number of arguments"));
+}
+
+TEST_F(FakeVehicleHardwareTest, testDumpInvalidOptions) {
+ std::vector<std::string> options;
+ options.push_back("--invalid");
+
+ DumpResult result = getHardware()->dump(options);
+ ASSERT_FALSE(result.callerShouldDumpState);
+ ASSERT_NE(result.buffer, "");
+ ASSERT_THAT(result.buffer, ContainsRegex("Invalid option: --invalid"));
+}
+
} // namespace fake
} // namespace vehicle
} // namespace automotive
diff --git a/automotive/vehicle/aidl/impl/vhal/include/DefaultVehicleHal.h b/automotive/vehicle/aidl/impl/vhal/include/DefaultVehicleHal.h
index e3267dd..5e7adfc 100644
--- a/automotive/vehicle/aidl/impl/vhal/include/DefaultVehicleHal.h
+++ b/automotive/vehicle/aidl/impl/vhal/include/DefaultVehicleHal.h
@@ -72,6 +72,7 @@
const std::vector<int32_t>& propIds) override;
::ndk::ScopedAStatus returnSharedMemory(const CallbackType& callback,
int64_t sharedMemoryId) override;
+ binder_status_t dump(int fd, const char** args, uint32_t numArgs) override;
IVehicleHardware* getHardware();
@@ -208,6 +209,8 @@
void monitorBinderLifeCycle(const CallbackType& callback);
+ bool checkDumpPermission();
+
template <class T>
static std::shared_ptr<T> getOrCreateClient(
std::unordered_map<const AIBinder*, std::shared_ptr<T>>* clients,
diff --git a/automotive/vehicle/aidl/impl/vhal/src/DefaultVehicleHal.cpp b/automotive/vehicle/aidl/impl/vhal/src/DefaultVehicleHal.cpp
index dd88979..c0a66da 100644
--- a/automotive/vehicle/aidl/impl/vhal/src/DefaultVehicleHal.cpp
+++ b/automotive/vehicle/aidl/impl/vhal/src/DefaultVehicleHal.cpp
@@ -24,6 +24,8 @@
#include <android-base/result.h>
#include <android-base/stringprintf.h>
+#include <android/binder_ibinder.h>
+#include <private/android_filesystem_config.h>
#include <utils/Log.h>
#include <utils/SystemClock.h>
@@ -718,6 +720,39 @@
mLinkToDeathImpl = std::move(impl);
}
+bool DefaultVehicleHal::checkDumpPermission() {
+ uid_t uid = AIBinder_getCallingUid();
+ return uid == AID_ROOT || uid == AID_SHELL || uid == AID_SYSTEM;
+}
+
+binder_status_t DefaultVehicleHal::dump(int fd, const char** args, uint32_t numArgs) {
+ if (!checkDumpPermission()) {
+ dprintf(fd, "Caller must be root, system or shell");
+ return STATUS_PERMISSION_DENIED;
+ }
+
+ std::vector<std::string> options;
+ for (uint32_t i = 0; i < numArgs; i++) {
+ options.push_back(args[i]);
+ }
+ DumpResult result = mVehicleHardware->dump(options);
+ dprintf(fd, "%s", (result.buffer + "\n").c_str());
+ if (!result.callerShouldDumpState) {
+ dprintf(fd, "Skip dumping Vehicle HAL State.\n");
+ return STATUS_OK;
+ }
+ dprintf(fd, "Vehicle HAL State: \n");
+ {
+ std::scoped_lock<std::mutex> lockGuard(mLock);
+ dprintf(fd, "Containing %zu property configs\n", mConfigsByPropId.size());
+ dprintf(fd, "Currently have %zu getValues clients\n", mGetValuesClients.size());
+ dprintf(fd, "Currently have %zu setValues clients\n", mSetValuesClients.size());
+ dprintf(fd, "Currently have %zu subscription clients\n",
+ mSubscriptionClients->countClients());
+ }
+ return STATUS_OK;
+}
+
} // namespace vehicle
} // namespace automotive
} // namespace hardware
diff --git a/automotive/vehicle/aidl/impl/vhal/test/DefaultVehicleHalTest.cpp b/automotive/vehicle/aidl/impl/vhal/test/DefaultVehicleHalTest.cpp
index 566f0bd..7443d5b 100644
--- a/automotive/vehicle/aidl/impl/vhal/test/DefaultVehicleHalTest.cpp
+++ b/automotive/vehicle/aidl/impl/vhal/test/DefaultVehicleHalTest.cpp
@@ -27,6 +27,7 @@
#include <android-base/thread_annotations.h>
#include <gmock/gmock.h>
#include <gtest/gtest.h>
+#include <sys/mman.h>
#include <utils/Log.h>
#include <utils/SystemClock.h>
@@ -75,6 +76,7 @@
using ::ndk::ScopedFileDescriptor;
using ::ndk::SpAIBinder;
+using ::testing::ContainsRegex;
using ::testing::Eq;
using ::testing::UnorderedElementsAre;
using ::testing::UnorderedElementsAreArray;
@@ -1545,6 +1547,45 @@
<< "expect OnBinderDied context to be deleted when binder is unlinked";
}
+TEST_F(DefaultVehicleHalTest, testDumpCallerShouldDump) {
+ std::string buffer = "Dump from hardware";
+ getHardware()->setDumpResult({
+ .callerShouldDumpState = true,
+ .buffer = buffer,
+ });
+ int fd = memfd_create("memfile", 0);
+ getClient()->dump(fd, nullptr, 0);
+
+ lseek(fd, 0, SEEK_SET);
+ char buf[10240] = {};
+ read(fd, buf, sizeof(buf));
+ close(fd);
+
+ std::string msg(buf);
+
+ ASSERT_THAT(msg, ContainsRegex(buffer + "\nVehicle HAL State: \n"));
+}
+
+TEST_F(DefaultVehicleHalTest, testDumpCallerShouldNotDump) {
+ std::string buffer = "Dump from hardware";
+ getHardware()->setDumpResult({
+ .callerShouldDumpState = false,
+ .buffer = buffer,
+ });
+ int fd = memfd_create("memfile", 0);
+ getClient()->dump(fd, nullptr, 0);
+
+ lseek(fd, 0, SEEK_SET);
+ char buf[10240] = {};
+ read(fd, buf, sizeof(buf));
+ close(fd);
+
+ std::string msg(buf);
+
+ ASSERT_THAT(msg, ContainsRegex(buffer));
+ ASSERT_EQ(msg.find("Vehicle HAL State: "), std::string::npos);
+}
+
} // namespace vehicle
} // namespace automotive
} // namespace hardware
diff --git a/automotive/vehicle/aidl/impl/vhal/test/MockVehicleHardware.cpp b/automotive/vehicle/aidl/impl/vhal/test/MockVehicleHardware.cpp
index eec32dd..66aef7c 100644
--- a/automotive/vehicle/aidl/impl/vhal/test/MockVehicleHardware.cpp
+++ b/automotive/vehicle/aidl/impl/vhal/test/MockVehicleHardware.cpp
@@ -71,13 +71,15 @@
&mGetValueResponses);
}
+void MockVehicleHardware::setDumpResult(DumpResult result) {
+ mDumpResult = result;
+}
+
DumpResult MockVehicleHardware::dump(const std::vector<std::string>&) {
- // TODO(b/200737967): mock this.
- return DumpResult{};
+ return mDumpResult;
}
StatusCode MockVehicleHardware::checkHealth() {
- // TODO(b/200737967): mock this.
return StatusCode::OK;
}
diff --git a/automotive/vehicle/aidl/impl/vhal/test/MockVehicleHardware.h b/automotive/vehicle/aidl/impl/vhal/test/MockVehicleHardware.h
index 0844de1..74d4fae 100644
--- a/automotive/vehicle/aidl/impl/vhal/test/MockVehicleHardware.h
+++ b/automotive/vehicle/aidl/impl/vhal/test/MockVehicleHardware.h
@@ -79,6 +79,7 @@
void setStatus(const char* functionName,
::aidl::android::hardware::automotive::vehicle::StatusCode status);
void setSleepTime(int64_t timeInNano);
+ void setDumpResult(DumpResult result);
private:
mutable std::mutex mLock;
@@ -114,6 +115,8 @@
const std::vector<RequestType>& requests,
std::list<std::vector<RequestType>>* storedRequests,
std::list<std::vector<ResultType>>* storedResponses) const REQUIRES(mLock);
+
+ DumpResult mDumpResult;
};
} // namespace vehicle