Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 93d36287 authored by Yu Shan's avatar Yu Shan
Browse files

Support debug dump

Test: atest FakeVehicleHardwareTest
Bug: 199314530
Change-Id: Ied0d1cd7c21e62a7db085f3b5be465cb0d576e73
parent 2e6d900f
Loading
Loading
Loading
Loading
+24 −0
Original line number Diff line number Diff line
@@ -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 @@ class FakeVehicleHardware final : public IVehicleHardware {
    ::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
+140 −3
Original line number Diff line number Diff line
@@ -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::VehiclePropertyStatus;
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 @@ StatusCode FakeVehicleHardware::getValues(std::shared_ptr<const GetValuesCallbac
    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;
}

+79 −0
Original line number Diff line number Diff line
@@ -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::VehicleProperty;
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, testInitialUserInfo) {
                         }));
}

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
+3 −0
Original line number Diff line number Diff line
@@ -72,6 +72,7 @@ class DefaultVehicleHal final : public ::aidl::android::hardware::automotive::ve
                                     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 @@ class DefaultVehicleHal final : public ::aidl::android::hardware::automotive::ve

    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,
+35 −0
Original line number Diff line number Diff line
@@ -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 @@ void DefaultVehicleHal::setLinkToDeathImpl(std::unique_ptr<ILinkToDeath> impl) {
    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
Loading