Adds check_interface_{restart,start,stop} check_builtins.

Includes refactoring out interface inheritance hierarchy logic to a new
interface_utils file.

Bug: 137397100
Test: 'm' with an init_rc that misspells an interface in an
interface_start, interface_restart, or interface_stop line.
Change-Id: I9f650289d64ae2b13435a81e1693c7ab5e6e9ecf
diff --git a/init/Android.bp b/init/Android.bp
index 6501900..3233cc3 100644
--- a/init/Android.bp
+++ b/init/Android.bp
@@ -83,6 +83,7 @@
         "libfscrypt",
         "libgsi",
         "libhidl-gen-utils",
+        "libjsoncpp",
         "libkeyutils",
         "liblog",
         "liblogwrap",
@@ -118,6 +119,7 @@
         "first_stage_mount.cpp",
         "import_parser.cpp",
         "init.cpp",
+        "interface_utils.cpp",
         "keychords.cpp",
         "modalias_handler.cpp",
         "mount_handler.cpp",
@@ -242,7 +244,10 @@
     name: "generated_stub_builtin_function_map",
     tool_files: ["host_builtin_map.py"],
     out: ["generated_stub_builtin_function_map.h"],
-    srcs: ["builtins.cpp", "check_builtins.cpp"],
+    srcs: [
+        "builtins.cpp",
+        "check_builtins.cpp",
+    ],
     cmd: "$(location host_builtin_map.py) --builtins $(location builtins.cpp) --check_builtins $(location check_builtins.cpp) > $(out)",
 }
 
@@ -278,6 +283,7 @@
         "epoll.cpp",
         "keychords.cpp",
         "import_parser.cpp",
+        "interface_utils.cpp",
         "host_import_parser.cpp",
         "host_init_verifier.cpp",
         "parser.cpp",
diff --git a/init/check_builtins.cpp b/init/check_builtins.cpp
index 3bd4774..771f1d7 100644
--- a/init/check_builtins.cpp
+++ b/init/check_builtins.cpp
@@ -29,6 +29,7 @@
 #include <android-base/strings.h>
 
 #include "builtin_arguments.h"
+#include "interface_utils.h"
 #include "rlimit_parser.h"
 #include "service.h"
 #include "util.h"
@@ -80,6 +81,21 @@
     return check_exec(std::move(args));
 }
 
+Result<void> check_interface_restart(const BuiltinArguments& args) {
+    if (auto result = IsKnownInterface(args[1]); !result) {
+        return result.error();
+    }
+    return {};
+}
+
+Result<void> check_interface_start(const BuiltinArguments& args) {
+    return check_interface_restart(std::move(args));
+}
+
+Result<void> check_interface_stop(const BuiltinArguments& args) {
+    return check_interface_restart(std::move(args));
+}
+
 Result<void> check_load_system_props(const BuiltinArguments& args) {
     return Error() << "'load_system_props' is deprecated";
 }
diff --git a/init/check_builtins.h b/init/check_builtins.h
index c974e88..4ff0d0c 100644
--- a/init/check_builtins.h
+++ b/init/check_builtins.h
@@ -25,6 +25,9 @@
 Result<void> check_chown(const BuiltinArguments& args);
 Result<void> check_exec(const BuiltinArguments& args);
 Result<void> check_exec_background(const BuiltinArguments& args);
+Result<void> check_interface_restart(const BuiltinArguments& args);
+Result<void> check_interface_start(const BuiltinArguments& args);
+Result<void> check_interface_stop(const BuiltinArguments& args);
 Result<void> check_load_system_props(const BuiltinArguments& args);
 Result<void> check_loglevel(const BuiltinArguments& args);
 Result<void> check_mkdir(const BuiltinArguments& args);
diff --git a/init/host_init_verifier.cpp b/init/host_init_verifier.cpp
index dce3eda..f2a2c26 100644
--- a/init/host_init_verifier.cpp
+++ b/init/host_init_verifier.cpp
@@ -30,7 +30,6 @@
 #include <android-base/logging.h>
 #include <android-base/parseint.h>
 #include <android-base/strings.h>
-#include <json/json.h>
 
 #include "action.h"
 #include "action_manager.h"
@@ -38,6 +37,7 @@
 #include "check_builtins.h"
 #include "host_import_parser.h"
 #include "host_init_stubs.h"
+#include "interface_utils.h"
 #include "parser.h"
 #include "result.h"
 #include "service.h"
@@ -132,35 +132,6 @@
     return nullptr;
 }
 
-static std::optional<android::init::InterfaceInheritanceHierarchyMap>
-ReadInterfaceInheritanceHierarchy(const std::string& interface_inheritance_hierarchy_file) {
-    if (interface_inheritance_hierarchy_file.empty()) {
-        LOG(WARNING) << "Missing an interface inheritance hierarchy file.";
-        return {};
-    }
-
-    Json::Value root;
-    Json::Reader reader;
-    std::ifstream stream(interface_inheritance_hierarchy_file);
-    if (!reader.parse(stream, root)) {
-        LOG(ERROR) << "Failed to read interface inheritance hierarchy file: "
-                   << interface_inheritance_hierarchy_file << "\n"
-                   << reader.getFormattedErrorMessages();
-        return {};
-    }
-
-    android::init::InterfaceInheritanceHierarchyMap result;
-    for (const Json::Value& entry : root) {
-        std::set<std::string> inherited_interfaces;
-        for (const Json::Value& intf : entry["inheritedInterfaces"]) {
-            inherited_interfaces.insert(intf.asString());
-        }
-        result[entry["interface"].asString()] = inherited_interfaces;
-    }
-
-    return result;
-}
-
 namespace android {
 namespace init {
 
@@ -222,16 +193,21 @@
         return EXIT_FAILURE;
     }
 
+    auto interface_inheritance_hierarchy_map =
+            ReadInterfaceInheritanceHierarchy(interface_inheritance_hierarchy_file);
+    if (!interface_inheritance_hierarchy_map) {
+        LOG(ERROR) << interface_inheritance_hierarchy_map.error();
+        return EXIT_FAILURE;
+    }
+    SetKnownInterfaces(*interface_inheritance_hierarchy_map);
+
     const BuiltinFunctionMap& function_map = GetBuiltinFunctionMap();
     Action::set_function_map(&function_map);
     ActionManager& am = ActionManager::GetInstance();
     ServiceList& sl = ServiceList::GetInstance();
     Parser parser;
-    parser.AddSectionParser(
-            "service",
-            std::make_unique<ServiceParser>(
-                    &sl, nullptr,
-                    ReadInterfaceInheritanceHierarchy(interface_inheritance_hierarchy_file)));
+    parser.AddSectionParser("service", std::make_unique<ServiceParser>(
+                                               &sl, nullptr, *interface_inheritance_hierarchy_map));
     parser.AddSectionParser("on", std::make_unique<ActionParser>(&am, nullptr));
     parser.AddSectionParser("import", std::make_unique<HostImportParser>());
 
diff --git a/init/interface_utils.cpp b/init/interface_utils.cpp
new file mode 100644
index 0000000..a54860f
--- /dev/null
+++ b/init/interface_utils.cpp
@@ -0,0 +1,149 @@
+/*
+ * 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 "interface_utils.h"
+
+#include <fstream>
+#include <sstream>
+
+#include <android-base/strings.h>
+#include <hidl-util/FqInstance.h>
+#include <json/json.h>
+
+using android::FqInstance;
+using android::FQName;
+using android::base::Error;
+
+namespace android {
+namespace init {
+
+namespace {
+
+std::string FQNamesToString(const std::set<FQName>& fqnames) {
+    std::set<std::string> fqname_strings;
+    for (const FQName& fqname : fqnames) {
+        fqname_strings.insert(fqname.string());
+    }
+    return android::base::Join(fqname_strings, " ");
+}
+
+}  // namespace
+
+Result<InterfaceInheritanceHierarchyMap> ReadInterfaceInheritanceHierarchy(
+        const std::string& path) {
+    Json::Value root;
+    Json::Reader reader;
+    std::ifstream stream(path);
+    if (!reader.parse(stream, root)) {
+        return Error() << "Failed to read interface inheritance hierarchy file: " << path << "\n"
+                       << reader.getFormattedErrorMessages();
+    }
+
+    InterfaceInheritanceHierarchyMap result;
+    for (const Json::Value& entry : root) {
+        std::set<FQName> inherited_interfaces;
+        for (const Json::Value& intf : entry["inheritedInterfaces"]) {
+            FQName fqname;
+            if (!fqname.setTo(intf.asString())) {
+                return Error() << "Unable to parse interface '" << intf.asString() << "'";
+            }
+            inherited_interfaces.insert(fqname);
+        }
+        std::string intf_string = entry["interface"].asString();
+        FQName fqname;
+        if (!fqname.setTo(intf_string)) {
+            return Error() << "Unable to parse interface '" << intf_string << "'";
+        }
+        result[fqname] = inherited_interfaces;
+    }
+
+    return result;
+}
+
+Result<void> CheckInterfaceInheritanceHierarchy(const std::set<std::string>& instances,
+                                                const InterfaceInheritanceHierarchyMap& hierarchy) {
+    std::set<FQName> interface_fqnames;
+    for (const std::string& instance : instances) {
+        FqInstance fqinstance;
+        if (!fqinstance.setTo(instance)) {
+            return Error() << "Unable to parse interface instance '" << instance << "'";
+        }
+        interface_fqnames.insert(fqinstance.getFqName());
+    }
+    return CheckInterfaceInheritanceHierarchy(interface_fqnames, hierarchy);
+}
+
+Result<void> CheckInterfaceInheritanceHierarchy(const std::set<FQName>& interfaces,
+                                                const InterfaceInheritanceHierarchyMap& hierarchy) {
+    std::ostringstream error_stream;
+    for (const FQName& intf : interfaces) {
+        if (hierarchy.count(intf) == 0) {
+            error_stream << "\nInterface is not in the known set of hidl_interfaces: '"
+                         << intf.string()
+                         << "'. Please ensure the interface is spelled correctly and built "
+                         << "by a hidl_interface target.";
+            continue;
+        }
+        const std::set<FQName>& required_interfaces = hierarchy.at(intf);
+        std::set<FQName> diff;
+        std::set_difference(required_interfaces.begin(), required_interfaces.end(),
+                            interfaces.begin(), interfaces.end(),
+                            std::inserter(diff, diff.begin()));
+        if (!diff.empty()) {
+            error_stream << "\nInterface '" << intf.string() << "' requires its full inheritance "
+                         << "hierarchy to be listed in this init_rc file. Missing "
+                         << "interfaces: [" << FQNamesToString(diff) << "]";
+        }
+    }
+    const std::string& errors = error_stream.str();
+    if (!errors.empty()) {
+        return Error() << errors;
+    }
+
+    return {};
+}
+
+std::optional<std::set<FQName>> known_interfaces;
+
+void SetKnownInterfaces(const InterfaceInheritanceHierarchyMap& hierarchy) {
+    known_interfaces = std::set<FQName>();
+    for (const auto& [intf, inherited_interfaces] : hierarchy) {
+        known_interfaces->insert(intf);
+    }
+}
+
+Result<void> IsKnownInterface(const std::string& instance) {
+    FqInstance fqinstance;
+    if (!fqinstance.setTo(instance)) {
+        return Error() << "Unable to parse interface instance '" << instance << "'";
+    }
+    return IsKnownInterface(fqinstance.getFqName());
+}
+
+Result<void> IsKnownInterface(const FQName& intf) {
+    if (!known_interfaces) {
+        return Error() << "No known interfaces have been loaded.";
+    }
+    if (known_interfaces->count(intf) == 0) {
+        return Error() << "Interface is not in the known set of hidl_interfaces: '" << intf.string()
+                       << "'. Please ensure the interface is spelled correctly and built "
+                       << "by a hidl_interface target.";
+    }
+    return {};
+}
+
+}  // namespace init
+}  // namespace android
diff --git a/init/interface_utils.h b/init/interface_utils.h
new file mode 100644
index 0000000..bd0c104
--- /dev/null
+++ b/init/interface_utils.h
@@ -0,0 +1,53 @@
+/*
+ * 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.
+ */
+
+#pragma once
+
+#include <map>
+#include <set>
+#include <string>
+
+#include <hidl-util/FQName.h>
+
+#include "result.h"
+
+namespace android {
+namespace init {
+
+using InterfaceInheritanceHierarchyMap = std::map<android::FQName, std::set<android::FQName>>;
+
+// Reads the HIDL interface inheritance hierarchy JSON file at the given path.
+Result<InterfaceInheritanceHierarchyMap> ReadInterfaceInheritanceHierarchy(const std::string& path);
+
+// For the given set of interfaces / interface instances, checks that each
+// interface's hierarchy of inherited interfaces is also included in the given
+// interface set. Uses the provided hierarchy data.
+Result<void> CheckInterfaceInheritanceHierarchy(const std::set<std::string>& instances,
+                                                const InterfaceInheritanceHierarchyMap& hierarchy);
+Result<void> CheckInterfaceInheritanceHierarchy(const std::set<android::FQName>& interfaces,
+                                                const InterfaceInheritanceHierarchyMap& hierarchy);
+
+// Saves the set of known interfaces using the provided HIDL interface
+// inheritance hierarchy.
+void SetKnownInterfaces(const InterfaceInheritanceHierarchyMap& hierarchy);
+
+// Checks if the provided interface is in the set of known interfaces. Returns
+// an empty Result if present, otherwise an Error.
+Result<void> IsKnownInterface(const std::string& instance);
+Result<void> IsKnownInterface(const FQName& intf);
+
+}  // namespace init
+}  // namespace android
diff --git a/init/service_parser.cpp b/init/service_parser.cpp
index e45e804..dd552fb 100644
--- a/init/service_parser.cpp
+++ b/init/service_parser.cpp
@@ -568,33 +568,10 @@
     }
 
     if (interface_inheritance_hierarchy_) {
-        std::set<std::string> interface_names;
-        for (const std::string& intf : service_->interfaces()) {
-            interface_names.insert(Split(intf, "/")[0]);
-        }
-        std::ostringstream error_stream;
-        for (const std::string& intf : interface_names) {
-            if (interface_inheritance_hierarchy_->count(intf) == 0) {
-                error_stream << "\nInterface is not in the known set of hidl_interfaces: '" << intf
-                             << "'. Please ensure the interface is spelled correctly and built "
-                             << "by a hidl_interface target.";
-                continue;
-            }
-            const std::set<std::string>& required_interfaces =
-                    (*interface_inheritance_hierarchy_)[intf];
-            std::set<std::string> diff;
-            std::set_difference(required_interfaces.begin(), required_interfaces.end(),
-                                interface_names.begin(), interface_names.end(),
-                                std::inserter(diff, diff.begin()));
-            if (!diff.empty()) {
-                error_stream << "\nInterface '" << intf << "' requires its full inheritance "
-                             << "hierarchy to be listed in this init_rc file. Missing "
-                             << "interfaces: [" << base::Join(diff, " ") << "]";
-            }
-        }
-        const std::string& errors = error_stream.str();
-        if (!errors.empty()) {
-            return Error() << errors;
+        if (const auto& check_hierarchy_result = CheckInterfaceInheritanceHierarchy(
+                    service_->interfaces(), *interface_inheritance_hierarchy_);
+            !check_hierarchy_result) {
+            return Error() << check_hierarchy_result.error();
         }
     }
 
diff --git a/init/service_parser.h b/init/service_parser.h
index 98ab15a..4729874 100644
--- a/init/service_parser.h
+++ b/init/service_parser.h
@@ -18,6 +18,7 @@
 
 #include <vector>
 
+#include "interface_utils.h"
 #include "parser.h"
 #include "service.h"
 #include "service_list.h"
@@ -26,8 +27,6 @@
 namespace android {
 namespace init {
 
-using InterfaceInheritanceHierarchyMap = std::map<std::string, std::set<std::string>>;
-
 class ServiceParser : public SectionParser {
   public:
     ServiceParser(