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

Commit 58936752 authored by Harpreet "Eli" Sangha's avatar Harpreet "Eli" Sangha Committed by android-build-merger
Browse files

Merge "Add CLI for Directly Invoking IDL APIs" am: ebbe2c45 am: b99cafae

am: a4c6b11d

Change-Id: Ia4e9916998babb871f59a02b8a23d1ac6690ba8a
parents bbdff6f5 a4c6b11d
Loading
Loading
Loading
Loading

cmds/idlcli/Android.bp

0 → 100644
+53 −0
Original line number Diff line number Diff line
// Copyright (C) 2019 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.

cc_defaults {
    name: "idlcli-defaults",
    shared_libs: [
        "android.hardware.vibrator@1.0",
        "android.hardware.vibrator@1.1",
        "android.hardware.vibrator@1.2",
        "android.hardware.vibrator@1.3",
        "libbase",
        "libhidlbase",
        "liblog",
        "libutils",
    ],
    cflags: [
        "-DLOG_TAG=\"idlcli\"",
    ],
}

cc_library {
    name: "libidlcli",
    defaults: ["idlcli-defaults"],
    srcs: [
        "CommandVibrator.cpp",
        "vibrator/CommandOff.cpp",
        "vibrator/CommandOn.cpp",
        "vibrator/CommandPerform.cpp",
        "vibrator/CommandSetAmplitude.cpp",
        "vibrator/CommandSetExternalControl.cpp",
        "vibrator/CommandSupportsAmplitudeControl.cpp",
        "vibrator/CommandSupportsExternalControl.cpp",
    ],
    visibility: [":__subpackages__"],
}

cc_binary {
    name: "idlcli",
    defaults: ["idlcli-defaults"],
    srcs: ["main.cpp"],
    whole_static_libs: ["libidlcli"],
}
+40 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2019 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.
 */

#include "utils.h"

namespace android {
namespace idlcli {

class IdlCli;

class CommandVibrator : public CommandWithSubcommands<CommandVibrator> {
    std::string getDescription() const override { return "Invoke Vibrator HIDL APIs."; }

    std::string getUsageSummary() const override { return "<api> [arguments]"; }

    UsageDetails getUsageDetails() const override {
        UsageDetails details{
                {"<api>", CommandRegistry<CommandVibrator>::List()},
        };
        return details;
    }
};

static const auto Command = CommandRegistry<IdlCli>::Register<CommandVibrator>("vibrator");

} // namespace idlcli
} // namespace android

cmds/idlcli/IdlCli.h

0 → 100644
+41 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2019 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.
 */

#ifndef FRAMEWORK_NATIVE_CMDS_IDLCLI_IDLCLI_H_
#define FRAMEWORK_NATIVE_CMDS_IDLCLI_IDLCLI_H_

#include "utils.h"

namespace android {
namespace idlcli {

class IdlCli : public CommandWithSubcommands<IdlCli> {
    std::string getDescription() const override { return "Invoke IDL APIs."; }

    std::string getUsageSummary() const override { return "<idl> [arguments]"; }

    UsageDetails getUsageDetails() const override {
        UsageDetails details{
                {"<idl>", CommandRegistry<IdlCli>::List()},
        };
        return details;
    }
};

} // namespace idlcli
} // namespace android

#endif // FRAMEWORK_NATIVE_CMDS_IDLCLI_IDLCLI_H_

cmds/idlcli/main.cpp

0 → 100644
+23 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2019 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.
 */

#include "IdlCli.h"
#include "utils.h"

int main(const int argc, const char* const argv[]) {
    using namespace ::android::idlcli;
    return IdlCli{}.main(Args{argc, argv});
}

cmds/idlcli/utils.h

0 → 100644
+270 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2019 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.
 */

#ifndef FRAMEWORK_NATIVE_CMDS_IDLCLI_UTILS_H_
#define FRAMEWORK_NATIVE_CMDS_IDLCLI_UTILS_H_

#include <hidl/HidlSupport.h>

#include <iomanip>
#include <iostream>
#include <map>
#include <sstream>
#include <string>
#include <vector>

namespace android {
namespace idlcli {

namespace overrides {

namespace details {

template <typename T>
inline std::istream &operator>>(std::istream &stream, T &out) {
    auto pos = stream.tellg();
    auto tmp = +out;
    auto min = +std::numeric_limits<T>::min();
    auto max = +std::numeric_limits<T>::max();
    stream >> tmp;
    if (!stream) {
        return stream;
    }
    if (tmp < min || tmp > max) {
        stream.seekg(pos);
        stream.setstate(std::ios_base::failbit);
        return stream;
    }
    out = tmp;
    return stream;
}

} // namespace details

// override for default behavior of treating as a character
inline std::istream &operator>>(std::istream &stream, int8_t &out) {
    return details::operator>>(stream, out);
}

// override for default behavior of treating as a character
inline std::istream &operator>>(std::istream &stream, uint8_t &out) {
    return details::operator>>(stream, out);
}

} // namespace overrides

template <typename T, typename R = hardware::hidl_enum_range<T>>
inline std::istream &operator>>(std::istream &stream, T &out) {
    using overrides::operator>>;
    auto validRange = R();
    auto pos = stream.tellg();
    std::underlying_type_t<T> in;
    T tmp;
    stream >> in;
    if (!stream) {
        return stream;
    }
    tmp = static_cast<T>(in);
    if (tmp < *validRange.begin() || tmp > *std::prev(validRange.end())) {
        stream.seekg(pos);
        stream.setstate(std::ios_base::failbit);
        return stream;
    }
    out = tmp;
    return stream;
}

enum Status : unsigned int {
    OK,
    USAGE,
    UNAVAILABLE,
    ERROR,
};

class Args {
public:
    Args(const int argc, const char *const argv[]) {
        for (int argi = 0; argi < argc; argi++) {
            mArgs.emplace_back(std::string_view(argv[argi]));
        }
    }

    template <typename T = std::string>
    std::optional<T> get() {
        return get<T>(false);
    }

    template <typename T = std::string>
    std::optional<T> pop() {
        return get<T>(true);
    }

    bool empty() { return mArgs.empty(); }

private:
    template <typename T>
    std::optional<T> get(bool erase) {
        using idlcli::operator>>;
        using overrides::operator>>;
        T retValue;

        if (mArgs.empty()) {
            return {};
        }

        std::stringstream stream{std::string{mArgs.front()}};
        stream >> std::setbase(0) >> retValue;
        if (!stream || !stream.eof()) {
            return {};
        }

        if (erase) {
            mArgs.erase(mArgs.begin());
        }

        return retValue;
    }

    std::vector<std::string_view> mArgs;
};

class Command {
protected:
    struct Usage {
        std::string name;
        std::vector<std::string> details;
    };
    using UsageDetails = std::vector<Usage>;

public:
    virtual ~Command() = default;

    Status main(Args &&args) {
        Status status = doArgsAndMain(std::move(args));
        if (status == USAGE) {
            printUsage();
            return ERROR;
        }
        if (status == UNAVAILABLE) {
            std::cerr << "The requested operation is unavailable." << std::endl;
            return ERROR;
        }
        return status;
    }

private:
    virtual std::string getDescription() const = 0;
    virtual std::string getUsageSummary() const = 0;
    virtual UsageDetails getUsageDetails() const = 0;
    virtual Status doArgs(Args &args) = 0;
    virtual Status doMain(Args &&args) = 0;

    void printUsage() const {
        std::cerr << "Description:\n  " << getDescription() << std::endl;
        std::cerr << "Usage:\n  " << mName << " " << getUsageSummary() << std::endl;

        std::cerr << "Details:" << std::endl;
        size_t entryNameWidth = 0;
        for (auto &entry : getUsageDetails()) {
            entryNameWidth = std::max(entryNameWidth, entry.name.length());
        }
        for (auto &entry : getUsageDetails()) {
            auto prefix = entry.name;
            for (auto &line : entry.details) {
                std::cerr << "  " << std::left << std::setw(entryNameWidth + 8) << prefix << line
                          << std::endl;
                prefix = "";
            }
        }
    }

    Status doArgsAndMain(Args &&args) {
        Status status;
        mName = *args.pop();
        if ((status = doArgs(args)) != OK) {
            return status;
        }
        if ((status = doMain(std::move(args))) != OK) {
            return status;
        }
        return OK;
    }

protected:
    std::string mName;
};

template <typename T>
class CommandRegistry {
private:
    using CommandCreator = std::function<std::unique_ptr<Command>()>;

public:
    template <typename U>
    static CommandCreator Register(const std::string name) {
        Instance()->mCommands[name] = [] { return std::make_unique<U>(); };
        return Instance()->mCommands[name];
    }

    static std::unique_ptr<Command> Create(const std::string name) {
        auto it = Instance()->mCommands.find(name);
        if (it == Instance()->mCommands.end()) {
            return nullptr;
        }
        return it->second();
    }

    static auto List() {
        std::vector<std::string> list;
        for (auto &it : Instance()->mCommands) {
            list.push_back(it.first);
        }
        std::sort(list.begin(), list.end());
        return list;
    }

private:
    static CommandRegistry *Instance() {
        static CommandRegistry sRegistry;
        return &sRegistry;
    }

private:
    std::map<const std::string, CommandCreator> mCommands;
};

template <typename T>
class CommandWithSubcommands : public Command {
private:
    Status doArgs(Args &args) override {
        mCommand = CommandRegistry<T>::Create(*args.get());
        if (!mCommand) {
            std::cerr << "Invalid Command!" << std::endl;
            return USAGE;
        }
        return OK;
    }

    Status doMain(Args &&args) override { return mCommand->main(std::move(args)); }

protected:
    std::unique_ptr<Command> mCommand;
};

} // namespace idlcli
} // namespace android

#endif // FRAMEWORK_NATIVE_CMDS_IDLCLI_UTILS_H_
Loading