From e055d73396ecfba1dafd41fd2ec0cb3353c81e6a Mon Sep 17 00:00:00 2001 From: Steven Moreland Date: Thu, 5 Oct 2017 18:50:22 -0700 Subject: [PATCH] init language extension for lazy HIDL services. This associates every service with a list of HIDL services it provides. If these are disabled, hwservicemanager will request for the service to startup. Bug: 64678982 Test: manual with the light service Change-Id: Ibf8a6f1cd38312c91c798b74574fa792f23c2df4 --- init/Android.bp | 3 ++ init/Android.mk | 1 + init/init.cpp | 90 +++++++++++++++++++++++++++++++++++++++++------- init/service.cpp | 33 ++++++++++++++++++ init/service.h | 4 +++ 5 files changed, 119 insertions(+), 12 deletions(-) diff --git a/init/Android.bp b/init/Android.bp index e906771da..8c4f005d8 100644 --- a/init/Android.bp +++ b/init/Android.bp @@ -89,6 +89,7 @@ cc_library_static { whole_static_libs: ["libcap"], static_libs: [ "libbase", + "libhidl-gen-utils", "libselinux", "liblog", "libprocessgroup", @@ -136,6 +137,7 @@ cc_binary { "libfs_mgr", "libfec", "libfec_rs", + "libhidl-gen-utils", "libsquashfs_utils", "liblogwrap", "libext4_utils", @@ -185,6 +187,7 @@ cc_test { ], static_libs: [ "libinit", + "libhidl-gen-utils", "libselinux", "libcrypto", "libprotobuf-cpp-lite", diff --git a/init/Android.mk b/init/Android.mk index dd0f1bfd7..44300f696 100644 --- a/init/Android.mk +++ b/init/Android.mk @@ -64,6 +64,7 @@ LOCAL_STATIC_LIBRARIES := \ libfs_mgr \ libfec \ libfec_rs \ + libhidl-gen-utils \ libsquashfs_utils \ liblogwrap \ libext4_utils \ diff --git a/init/init.cpp b/init/init.cpp index 51a98a2ea..571da7cdb 100644 --- a/init/init.cpp +++ b/init/init.cpp @@ -202,24 +202,90 @@ static std::optional RestartProcesses() { return next_process_restart_time; } +static Result DoControlStart(Service* service) { + return service->Start(); +} + +static Result DoControlStop(Service* service) { + service->Stop(); + return Success(); +} + +static Result DoControlRestart(Service* service) { + service->Restart(); + return Success(); +} + +enum class ControlTarget { + SERVICE, // function gets called for the named service + INTERFACE, // action gets called for every service that holds this interface +}; + +struct ControlMessageFunction { + ControlTarget target; + std::function(Service*)> action; +}; + +static const std::map& get_control_message_map() { + // clang-format off + static const std::map control_message_functions = { + {"start", {ControlTarget::SERVICE, DoControlStart}}, + {"stop", {ControlTarget::SERVICE, DoControlStop}}, + {"restart", {ControlTarget::SERVICE, DoControlRestart}}, + {"interface_start", {ControlTarget::INTERFACE, DoControlStart}}, + {"interface_stop", {ControlTarget::INTERFACE, DoControlStop}}, + {"interface_restart", {ControlTarget::INTERFACE, DoControlRestart}}, + }; + // clang-format on + + return control_message_functions; +} + void handle_control_message(const std::string& msg, const std::string& name) { - Service* svc = ServiceList::GetInstance().FindService(name); - if (svc == nullptr) { - LOG(ERROR) << "no such service '" << name << "'"; + const auto& map = get_control_message_map(); + const auto it = map.find(msg); + + if (it == map.end()) { + LOG(ERROR) << "Unknown control msg '" << msg << "'"; return; } - if (msg == "start") { - if (auto result = svc->Start(); !result) { - LOG(ERROR) << "Could not ctl.start service '" << name << "': " << result.error(); + const ControlMessageFunction& function = it->second; + + if (function.target == ControlTarget::SERVICE) { + Service* svc = ServiceList::GetInstance().FindService(name); + if (svc == nullptr) { + LOG(ERROR) << "No such service '" << name << "' for ctl." << msg; + return; } - } else if (msg == "stop") { - svc->Stop(); - } else if (msg == "restart") { - svc->Restart(); - } else { - LOG(ERROR) << "unknown control msg '" << msg << "'"; + if (auto result = function.action(svc); !result) { + LOG(ERROR) << "Could not ctl." << msg << " for service " << name << ": " + << result.error(); + } + + return; } + + if (function.target == ControlTarget::INTERFACE) { + for (const auto& svc : ServiceList::GetInstance()) { + if (svc->interfaces().count(name) == 0) { + continue; + } + + if (auto result = function.action(svc.get()); !result) { + LOG(ERROR) << "Could not handle ctl." << msg << " for service " << svc->name() + << " with interface " << name << ": " << result.error(); + } + + return; + } + + LOG(ERROR) << "Could not find service hosting interface " << name; + return; + } + + LOG(ERROR) << "Invalid function target from static map key '" << msg + << "': " << static_cast::type>(function.target); } static Result wait_for_coldboot_done_action(const BuiltinArguments& args) { diff --git a/init/service.cpp b/init/service.cpp index b339bc0b6..765b61e56 100644 --- a/init/service.cpp +++ b/init/service.cpp @@ -37,6 +37,7 @@ #include #include #include +#include #include #include #include @@ -418,6 +419,37 @@ Result Service::ParsePriority(const std::vector& args) { return Success(); } +Result Service::ParseInterface(const std::vector& args) { + const std::string& interface_name = args[1]; + const std::string& instance_name = args[2]; + + const FQName fq_name = FQName(interface_name); + if (!fq_name.isValid()) { + return Error() << "Invalid fully-qualified name for interface '" << interface_name << "'"; + } + + if (!fq_name.isFullyQualified()) { + return Error() << "Interface name not fully-qualified '" << interface_name << "'"; + } + + if (fq_name.isValidValueName()) { + return Error() << "Interface name must not be a value name '" << interface_name << "'"; + } + + const std::string fullname = interface_name + "/" + instance_name; + + for (const auto& svc : ServiceList::GetInstance()) { + if (svc->interfaces().count(fullname) > 0) { + return Error() << "Interface '" << fullname << "' redefined in " << name() + << " but is already defined by " << svc->name(); + } + } + + interfaces_.insert(fullname); + + return Success(); +} + Result Service::ParseIoprio(const std::vector& args) { if (!ParseInt(args[2], &ioprio_pri_, 0, 7)) { return Error() << "priority value must be range 0 - 7"; @@ -619,6 +651,7 @@ const Service::OptionParserMap::Map& Service::OptionParserMap::map() const { {"critical", {0, 0, &Service::ParseCritical}}, {"disabled", {0, 0, &Service::ParseDisabled}}, {"group", {1, NR_SVC_SUPP_GIDS + 1, &Service::ParseGroup}}, + {"interface", {2, 2, &Service::ParseInterface}}, {"ioprio", {2, 2, &Service::ParseIoprio}}, {"priority", {1, 1, &Service::ParsePriority}}, {"keycodes", {1, kMax, &Service::ParseKeycodes}}, diff --git a/init/service.h b/init/service.h index 89dd7806b..593f78289 100644 --- a/init/service.h +++ b/init/service.h @@ -108,6 +108,7 @@ class Service { void set_keychord_id(int keychord_id) { keychord_id_ = keychord_id; } IoSchedClass ioprio_class() const { return ioprio_class_; } int ioprio_pri() const { return ioprio_pri_; } + const std::set& interfaces() const { return interfaces_; } int priority() const { return priority_; } int oom_score_adjust() const { return oom_score_adjust_; } bool process_cgroup_empty() const { return process_cgroup_empty_; } @@ -132,6 +133,7 @@ class Service { Result ParseDisabled(const std::vector& args); Result ParseGroup(const std::vector& args); Result ParsePriority(const std::vector& args); + Result ParseInterface(const std::vector& args); Result ParseIoprio(const std::vector& args); Result ParseKeycodes(const std::vector& args); Result ParseOneshot(const std::vector& args); @@ -181,6 +183,8 @@ class Service { std::vector writepid_files_; + std::set interfaces_; // e.g. some.package.foo@1.0::IBaz/instance-name + // keycodes for triggering this service via /dev/keychord std::vector keycodes_; int keychord_id_;