| // Copyright 2019 The Fuchsia Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include <fuchsia/ui/activity/control/cpp/fidl.h> |
| #include <fuchsia/ui/activity/cpp/fidl.h> |
| #include <lib/async-loop/cpp/loop.h> |
| #include <lib/async-loop/default.h> |
| #include <lib/async/cpp/task.h> |
| #include <lib/async/cpp/time.h> |
| #include <lib/async/default.h> |
| #include <lib/async/dispatcher.h> |
| #include <lib/cmdline/args_parser.h> |
| #include <lib/fidl/cpp/binding.h> |
| #include <lib/sys/cpp/component_context.h> |
| #include <lib/syslog/cpp/macros.h> |
| #include <lib/zx/clock.h> |
| #include <zircon/errors.h> |
| #include <zircon/status.h> |
| |
| #include <memory> |
| #include <random> |
| #include <variant> |
| |
| #include "src/lib/fxl/log_settings_command_line.h" |
| #include "src/lib/fxl/strings/string_printf.h" |
| |
| namespace activity_ctl { |
| |
| const char kHelp[] = R"(activity_ctl <command> [ <command_args> ] |
| |
| activity_ctl is a command line utility for interacting with the Activity |
| Service. This utility can provide input to the activity service or listen |
| to the system's state activity state. |
| |
| Commands |
| |
| discrete_activity - Send a discrete activity |
| force_state <state> - Force the activity service into a state |
| ongoing_activity - Initiate an ongoing activity, ending when the utility |
| exits |
| watch_state - Listen for changes to the system's activity state |
| )"; |
| |
| const char kHelpHelp[] = R"(--help (-h) |
| Prints this help and exits)"; |
| |
| constexpr const char kCommandForceState[] = "force_state"; |
| constexpr const char kCommandWatchState[] = "watch_state"; |
| constexpr const char kCommandDiscreteActivity[] = "discrete_activity"; |
| constexpr const char kCommandOngoingActivity[] = "ongoing_activity"; |
| |
| enum Command { Unknown, ForceState, WatchState, SendDiscreteActivity, SendOngoingActivity }; |
| |
| Command ParseCommand(std::string_view cmd) { |
| if (cmd == kCommandForceState) |
| return Command::ForceState; |
| if (cmd == kCommandWatchState) |
| return Command::WatchState; |
| if (cmd == kCommandDiscreteActivity) |
| return Command::SendDiscreteActivity; |
| if (cmd == kCommandOngoingActivity) |
| return Command::SendOngoingActivity; |
| return Command::Unknown; |
| } |
| |
| std::variant<std::vector<std::string>, cmdline::Status> ParseCommandLine(int argc, |
| const char* argv[]) { |
| cmdline::GeneralArgsParser parser; |
| |
| bool requested_help = false; |
| parser.AddGeneralSwitch("help", 'h', kHelpHelp, [&requested_help]() { requested_help = true; }); |
| |
| std::vector<std::string> params; |
| cmdline::Status status = parser.ParseGeneral(argc, argv, ¶ms); |
| if (status.has_error()) |
| return status; |
| |
| if (requested_help || params.empty()) |
| return cmdline::Status::Error(kHelp); |
| |
| return params; |
| } |
| |
| fuchsia::ui::activity::State ParseState(std::string_view state) { |
| if (state == "IDLE") |
| return fuchsia::ui::activity::State::IDLE; |
| if (state == "ACTIVE") |
| return fuchsia::ui::activity::State::ACTIVE; |
| return fuchsia::ui::activity::State::UNKNOWN; |
| } |
| |
| std::string StateToString(fuchsia::ui::activity::State state) { |
| if (state == fuchsia::ui::activity::State::IDLE) |
| return "IDLE"; |
| if (state == fuchsia::ui::activity::State::ACTIVE) |
| return "ACTIVE"; |
| return "UNKNOWN"; |
| } |
| |
| class LoggingListener : public fuchsia::ui::activity::Listener { |
| public: |
| fidl::InterfaceHandle<fuchsia::ui::activity::Listener> GetHandle(async_dispatcher_t* dispatcher) { |
| return binding_.NewBinding(dispatcher); |
| } |
| |
| void OnStateChanged(fuchsia::ui::activity::State state, zx_time_t transition_time, |
| OnStateChangedCallback callback) override { |
| printf("[%lu] %s -> %s\n", transition_time, StateToString(state_).c_str(), |
| StateToString(state).c_str()); |
| state_ = state; |
| callback(); |
| } |
| |
| private: |
| fuchsia::ui::activity::State state_; |
| fidl::Binding<fuchsia::ui::activity::Listener> binding_{this}; |
| }; |
| |
| class ActivityCtl { |
| public: |
| ActivityCtl(std::unique_ptr<sys::ComponentContext> startup_context, |
| async_dispatcher_t* dispatcher, fit::closure quit_callback) |
| : context_(std::move(startup_context)), |
| quit_callback_(std::move(quit_callback)), |
| dispatcher_(dispatcher), |
| random_(static_cast<uint32_t>(zx::clock::get_monotonic().get())) {} |
| |
| zx_status_t RunCommand(Command cmd, std::vector<std::string> args) { |
| switch (cmd) { |
| case Command::ForceState: { |
| if (args.empty()) |
| return ZX_ERR_INVALID_ARGS; |
| auto state = ParseState(args[0]); |
| if (state == fuchsia::ui::activity::State::UNKNOWN) |
| return ZX_ERR_INVALID_ARGS; |
| ForceState(state); |
| break; |
| } |
| case Command::WatchState: { |
| WatchState(); |
| break; |
| } |
| case Command::SendDiscreteActivity: { |
| SendDiscreteActivity(); |
| break; |
| } |
| case Command::SendOngoingActivity: { |
| SendOngoingActivity(); |
| break; |
| } |
| case Command::Unknown: |
| default: { |
| return ZX_ERR_INVALID_ARGS; |
| } |
| } |
| return ZX_OK; |
| } |
| |
| void ForceState(fuchsia::ui::activity::State state) { |
| control_conn_ = context_->svc()->Connect<fuchsia::ui::activity::control::Control>(); |
| (*control_conn_)->SetState(state); |
| async::PostTask(dispatcher_, std::move(quit_callback_)); |
| } |
| |
| void WatchState() { |
| provider_conn_ = context_->svc()->Connect<fuchsia::ui::activity::Provider>(); |
| (*provider_conn_)->WatchState(listener_.GetHandle(dispatcher_)); |
| } |
| |
| void SendDiscreteActivity() {} |
| |
| void SendOngoingActivity() {} |
| |
| private: |
| std::optional<fidl::InterfacePtr<fuchsia::ui::activity::Provider>> provider_conn_; |
| std::optional<fidl::InterfacePtr<fuchsia::ui::activity::control::Control>> control_conn_; |
| LoggingListener listener_; |
| std::unique_ptr<sys::ComponentContext> context_; |
| fit::closure quit_callback_; |
| async_dispatcher_t* dispatcher_; |
| std::default_random_engine random_; |
| }; |
| |
| } // namespace activity_ctl |
| |
| int main(int argc, const char** argv) { |
| auto status_or_params = activity_ctl::ParseCommandLine(argc, argv); |
| if (std::holds_alternative<cmdline::Status>(status_or_params)) { |
| fprintf(stderr, "%s\n", std::get<cmdline::Status>(status_or_params).error_message().c_str()); |
| return 1; |
| } |
| auto params = std::get<std::vector<std::string>>(status_or_params); |
| |
| async::Loop loop(&kAsyncLoopConfigAttachToCurrentThread); |
| async_set_default_dispatcher(loop.dispatcher()); |
| std::unique_ptr<sys::ComponentContext> startup_context = |
| sys::ComponentContext::CreateAndServeOutgoingDirectory(); |
| |
| activity_ctl::ActivityCtl ctl{std::move(startup_context), loop.dispatcher(), |
| [&loop]() { loop.Quit(); }}; |
| auto cmd = activity_ctl::ParseCommand(params[0]); |
| if (cmd == activity_ctl::Command::Unknown) { |
| fprintf(stderr, "Unknown command: %s\n%s\n", params[0].c_str(), activity_ctl::kHelp); |
| return 1; |
| } |
| params.erase(params.begin()); |
| if (auto status = ctl.RunCommand(cmd, params) != ZX_OK) { |
| fprintf(stderr, "Error: %s\n%s\n", zx_status_get_string(status), activity_ctl::kHelp); |
| return 1; |
| } |
| |
| loop.Run(); |
| async_set_default_dispatcher(nullptr); |
| return 0; |
| } |