Query methods for VINTF native instances

isDeclared(name), getDeclaredInstances(interface),
updatableViaApex(name), and getUpdatableNames(apexName) are extended to
support native instances when
- "name" is formatted as e.g. "mapper/minigbm",
- "interface" is formatted as e.g. "mapper"

Bug: 316051788
Test: atest servicemanager_test
Test: atest --host servicemanager_unittest
Change-Id: I0520888dbf5d30c66b97058f45cd6828731ca8bc
diff --git a/cmds/servicemanager/Android.bp b/cmds/servicemanager/Android.bp
index e00c2a2..3897197 100644
--- a/cmds/servicemanager/Android.bp
+++ b/cmds/servicemanager/Android.bp
@@ -95,6 +95,16 @@
     static_libs: ["libgmock"],
 }
 
+cc_test_host {
+    name: "servicemanager_unittest",
+    test_suites: ["general-tests"],
+    defaults: ["servicemanager_defaults"],
+    srcs: [
+        "ServiceManagerUnittest.cpp",
+    ],
+    static_libs: ["libgmock"],
+}
+
 cc_fuzz {
     name: "servicemanager_fuzzer",
     defaults: [
diff --git a/cmds/servicemanager/NameUtil.h b/cmds/servicemanager/NameUtil.h
new file mode 100644
index 0000000..b080939
--- /dev/null
+++ b/cmds/servicemanager/NameUtil.h
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2023 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 <string>
+#include <string_view>
+
+#include <android-base/strings.h>
+
+namespace android {
+
+#ifndef VENDORSERVICEMANAGER
+
+struct NativeName {
+    std::string package;
+    std::string instance;
+
+    // Parse {package}/{instance}
+    static bool fill(std::string_view name, NativeName* nname) {
+        size_t slash = name.find('/');
+        if (slash == std::string_view::npos) {
+            return false;
+        }
+        // no extra slashes
+        if (name.find('/', slash + 1) != std::string_view::npos) {
+            return false;
+        }
+        // every part should be non-empty
+        if (slash == 0 || slash + 1 == name.size()) {
+            return false;
+        }
+        // no dots in package
+        if (name.rfind('.', slash) != std::string_view::npos) {
+            return false;
+        }
+        nname->package = name.substr(0, slash);
+        nname->instance = name.substr(slash + 1);
+        return true;
+    }
+};
+
+#endif
+
+} // namespace android
diff --git a/cmds/servicemanager/ServiceManager.cpp b/cmds/servicemanager/ServiceManager.cpp
index f2f0a0f..bf85e61 100644
--- a/cmds/servicemanager/ServiceManager.cpp
+++ b/cmds/servicemanager/ServiceManager.cpp
@@ -35,6 +35,8 @@
 #include <vintf/constants.h>
 #endif  // !VENDORSERVICEMANAGER
 
+#include "NameUtil.h"
+
 using ::android::binder::Status;
 using ::android::internal::Stability;
 
@@ -84,6 +86,10 @@
     return false;
 }
 
+static std::string getNativeInstanceName(const vintf::ManifestInstance& instance) {
+    return instance.package() + "/" + instance.instance();
+}
+
 struct AidlName {
     std::string package;
     std::string iface;
@@ -105,7 +111,26 @@
     }
 };
 
+static std::string getAidlInstanceName(const vintf::ManifestInstance& instance) {
+    return instance.package() + "." + instance.interface() + "/" + instance.instance();
+}
+
 static bool isVintfDeclared(const std::string& name) {
+    NativeName nname;
+    if (NativeName::fill(name, &nname)) {
+        bool found = forEachManifest([&](const ManifestWithDescription& mwd) {
+            if (mwd.manifest->hasNativeInstance(nname.package, nname.instance)) {
+                ALOGI("Found %s in %s VINTF manifest.", name.c_str(), mwd.description);
+                return true; // break
+            }
+            return false; // continue
+        });
+        if (!found) {
+            ALOGI("Could not find %s in the VINTF manifest.", name.c_str());
+        }
+        return found;
+    }
+
     AidlName aname;
     if (!AidlName::fill(name, &aname)) return false;
 
@@ -144,6 +169,24 @@
 }
 
 static std::optional<std::string> getVintfUpdatableApex(const std::string& name) {
+    NativeName nname;
+    if (NativeName::fill(name, &nname)) {
+        std::optional<std::string> updatableViaApex;
+
+        forEachManifest([&](const ManifestWithDescription& mwd) {
+            bool cont = mwd.manifest->forEachInstance([&](const auto& manifestInstance) {
+                if (manifestInstance.format() != vintf::HalFormat::NATIVE) return true;
+                if (manifestInstance.package() != nname.package) return true;
+                if (manifestInstance.instance() != nname.instance) return true;
+                updatableViaApex = manifestInstance.updatableViaApex();
+                return false; // break (libvintf uses opposite convention)
+            });
+            return !cont;
+        });
+
+        return updatableViaApex;
+    }
+
     AidlName aname;
     if (!AidlName::fill(name, &aname)) return std::nullopt;
 
@@ -164,24 +207,25 @@
     return updatableViaApex;
 }
 
-static std::vector<std::string> getVintfUpdatableInstances(const std::string& apexName) {
-    std::vector<std::string> instances;
+static std::vector<std::string> getVintfUpdatableNames(const std::string& apexName) {
+    std::vector<std::string> names;
 
     forEachManifest([&](const ManifestWithDescription& mwd) {
         mwd.manifest->forEachInstance([&](const auto& manifestInstance) {
-            if (manifestInstance.format() == vintf::HalFormat::AIDL &&
-                manifestInstance.updatableViaApex().has_value() &&
+            if (manifestInstance.updatableViaApex().has_value() &&
                 manifestInstance.updatableViaApex().value() == apexName) {
-                std::string aname = manifestInstance.package() + "." +
-                        manifestInstance.interface() + "/" + manifestInstance.instance();
-                instances.push_back(aname);
+                if (manifestInstance.format() == vintf::HalFormat::NATIVE) {
+                    names.push_back(getNativeInstanceName(manifestInstance));
+                } else if (manifestInstance.format() == vintf::HalFormat::AIDL) {
+                    names.push_back(getAidlInstanceName(manifestInstance));
+                }
             }
             return true; // continue (libvintf uses opposite convention)
         });
         return false; // continue
     });
 
-    return instances;
+    return names;
 }
 
 static std::optional<ConnectionInfo> getVintfConnectionInfo(const std::string& name) {
@@ -216,6 +260,18 @@
 static std::vector<std::string> getVintfInstances(const std::string& interface) {
     size_t lastDot = interface.rfind('.');
     if (lastDot == std::string::npos) {
+        // This might be a package for native instance.
+        std::vector<std::string> ret;
+        (void)forEachManifest([&](const ManifestWithDescription& mwd) {
+            auto instances = mwd.manifest->getNativeInstances(interface);
+            ret.insert(ret.end(), instances.begin(), instances.end());
+            return false; // continue
+        });
+        // If found, return it without error log.
+        if (!ret.empty()) {
+            return ret;
+        }
+
         ALOGE("VINTF interfaces require names in Java package format (e.g. some.package.foo.IFoo) "
               "but got: %s",
               interface.c_str());
@@ -593,20 +649,20 @@
                                          std::vector<std::string>* outReturn) {
     auto ctx = mAccess->getCallingContext();
 
-    std::vector<std::string> apexUpdatableInstances;
+    std::vector<std::string> apexUpdatableNames;
 #ifndef VENDORSERVICEMANAGER
-    apexUpdatableInstances = getVintfUpdatableInstances(apexName);
+    apexUpdatableNames = getVintfUpdatableNames(apexName);
 #endif
 
     outReturn->clear();
 
-    for (const std::string& instance : apexUpdatableInstances) {
-        if (mAccess->canFind(ctx, instance)) {
-            outReturn->push_back(instance);
+    for (const std::string& name : apexUpdatableNames) {
+        if (mAccess->canFind(ctx, name)) {
+            outReturn->push_back(name);
         }
     }
 
-    if (outReturn->size() == 0 && apexUpdatableInstances.size() != 0) {
+    if (outReturn->size() == 0 && apexUpdatableNames.size() != 0) {
         return Status::fromExceptionCode(Status::EX_SECURITY, "SELinux denied.");
     }
 
diff --git a/cmds/servicemanager/ServiceManagerUnittest.cpp b/cmds/servicemanager/ServiceManagerUnittest.cpp
new file mode 100644
index 0000000..39d20b0
--- /dev/null
+++ b/cmds/servicemanager/ServiceManagerUnittest.cpp
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2023 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 <gtest/gtest.h>
+
+#include "NameUtil.h"
+
+namespace android {
+
+TEST(ServiceManager, NativeName) {
+    NativeName nname;
+    EXPECT_TRUE(NativeName::fill("mapper/default", &nname));
+    EXPECT_EQ("mapper", nname.package);
+    EXPECT_EQ("default", nname.instance);
+}
+
+TEST(ServiceManager, NativeName_Malformed) {
+    NativeName nname;
+    EXPECT_FALSE(NativeName::fill("mapper", &nname));
+    EXPECT_FALSE(NativeName::fill("mapper/", &nname));
+    EXPECT_FALSE(NativeName::fill("/default", &nname));
+    EXPECT_FALSE(NativeName::fill("mapper/default/0", &nname));
+    EXPECT_FALSE(NativeName::fill("aidl.like.IType/default", &nname));
+}
+
+} // namespace android
diff --git a/cmds/servicemanager/test_sm.cpp b/cmds/servicemanager/test_sm.cpp
index 97e500d..b575053 100644
--- a/cmds/servicemanager/test_sm.cpp
+++ b/cmds/servicemanager/test_sm.cpp
@@ -361,6 +361,24 @@
     EXPECT_EQ(std::vector<std::string>{}, names);
 }
 
+TEST(Vintf, IsDeclared_native) {
+    if (!isCuttlefishPhone()) GTEST_SKIP() << "Skipping non-Cuttlefish-phone devices";
+
+    auto sm = getPermissiveServiceManager();
+    bool declared = false;
+    EXPECT_TRUE(sm->isDeclared("mapper/minigbm", &declared).isOk());
+    EXPECT_TRUE(declared);
+}
+
+TEST(Vintf, GetDeclaredInstances_native) {
+    if (!isCuttlefishPhone()) GTEST_SKIP() << "Skipping non-Cuttlefish-phone devices";
+
+    auto sm = getPermissiveServiceManager();
+    std::vector<std::string> instances;
+    EXPECT_TRUE(sm->getDeclaredInstances("mapper", &instances).isOk());
+    EXPECT_EQ(std::vector<std::string>{"minigbm"}, instances);
+}
+
 class CallbackHistorian : public BnServiceCallback {
     Status onRegistration(const std::string& name, const sp<IBinder>& binder) override {
         registrations.push_back(name);