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

Commit 94ae84ff authored by Jooyung Han's avatar Jooyung Han Committed by Gerrit Code Review
Browse files

Merge "Query methods for VINTF native instances" into main

parents 77f3da22 205e282f
Loading
Loading
Loading
Loading
+10 −0
Original line number Diff line number Diff line
@@ -95,6 +95,16 @@ cc_test {
    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: [
+58 −0
Original line number Diff line number Diff line
/*
 * 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
+70 −14
Original line number Diff line number Diff line
@@ -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 @@ static bool forEachManifest(const std::function<bool(const ManifestWithDescripti
    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 @@ struct AidlName {
    }
};

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 bool isVintfDeclared(const std::string& name) {
}

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 @@ static std::optional<std::string> getVintfUpdatableApex(const std::string& name)
    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::optional<ConnectionInfo> getVintfConnectionInfo(const std::string& n
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 @@ Status ServiceManager::getUpdatableNames([[maybe_unused]] const std::string& ape
                                         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.");
    }

+39 −0
Original line number Diff line number Diff line
/*
 * 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
+18 −0
Original line number Diff line number Diff line
@@ -361,6 +361,24 @@ TEST(Vintf, GetUpdatableNames_InvalidApexNameReturnsEmpty) {
    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);