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

Commit 169bb8f0 authored by Steven Moreland's avatar Steven Moreland
Browse files

libbinder_ndk: test Bp destruction

When sending a binder to another process, there are two things that
delay the destruction:
- there is a separate queue, flushed with 'flushCommands' that must be
emptied in the remote process
- the client thread must process the refcount loss asynchronously

This CL works around both of these problems by explicitly flushing
commands and waiting for the thread to process the refcount.

Bug: 148287051
Test: libbinder_ndk_unit_test
Change-Id: I2687b429faae659d80406f0b418c96a1eb40e9bd
parent 56a1e4fc
Loading
Loading
Loading
Loading
+11 −0
Original line number Diff line number Diff line
@@ -59,6 +59,9 @@ cc_test {
    name: "libbinder_ndk_unit_test",
    defaults: ["test_libbinder_ndk_test_defaults"],
    srcs: ["libbinder_ndk_unit_test.cpp"],
    static_libs: [
        "IBinderNdkUnitTest-ndk_platform",
    ],
    test_suites: ["general-tests"],
    require_root: true,

@@ -93,3 +96,11 @@ aidl_interface {
        "IBinderVendorDoubleLoadTest.aidl",
    ],
}

aidl_interface {
    name: "IBinderNdkUnitTest",
    srcs: [
        "IBinderNdkUnitTest.aidl",
        "IEmpty.aidl",
    ],
}
+27 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2020 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.
 */

// This AIDL is to test things that can't be tested in CtsNdkBinderTestCases
// because it requires libbinder_ndk implementation details or APIs not
// available to apps. Please prefer adding tests to CtsNdkBinderTestCases
// over here.

import IEmpty;

interface IBinderNdkUnitTest {
    void takeInterface(IEmpty test);
    void forceFlushCommands();
}
+17 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2020 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.
 */

interface IEmpty { }
+78 −3
Original line number Diff line number Diff line
@@ -14,6 +14,8 @@
 * limitations under the License.
 */

#include <aidl/BnBinderNdkUnitTest.h>
#include <aidl/BnEmpty.h>
#include <android-base/logging.h>
#include <android/binder_ibinder_jni.h>
#include <android/binder_manager.h>
@@ -21,6 +23,10 @@
#include <gtest/gtest.h>
#include <iface/iface.h>

// warning: this is assuming that libbinder_ndk is using the same copy
// of libbinder that we are.
#include <binder/IPCThreadState.h>

#include <sys/prctl.h>
#include <chrono>
#include <condition_variable>
@@ -29,7 +35,38 @@
using ::android::sp;

constexpr char kExistingNonNdkService[] = "SurfaceFlinger";
constexpr char kBinderNdkUnitTestService[] = "BinderNdkUnitTest";

class MyBinderNdkUnitTest : public aidl::BnBinderNdkUnitTest {
    ndk::ScopedAStatus takeInterface(const std::shared_ptr<aidl::IEmpty>& empty) {
        (void)empty;
        return ndk::ScopedAStatus::ok();
    }
    ndk::ScopedAStatus forceFlushCommands() {
        // warning: this is assuming that libbinder_ndk is using the same copy
        // of libbinder that we are.
        android::IPCThreadState::self()->flushCommands();
        return ndk::ScopedAStatus::ok();
    }
};

int generatedService() {
    ABinderProcess_setThreadPoolMaxThreadCount(0);

    auto service = ndk::SharedRefBase::make<MyBinderNdkUnitTest>();
    binder_status_t status =
            AServiceManager_addService(service->asBinder().get(), kBinderNdkUnitTestService);

    if (status != STATUS_OK) {
        LOG(FATAL) << "Could not register: " << status << " " << kBinderNdkUnitTestService;
    }

    ABinderProcess_joinThreadPool();

    return 1;  // should not return
}

// manually-written parceling class considered bad practice
class MyFoo : public IFoo {
    binder_status_t doubleNumber(int32_t in, int32_t* out) override {
        *out = 2 * in;
@@ -43,7 +80,7 @@ class MyFoo : public IFoo {
    }
};

int service(const char* instance) {
int manualService(const char* instance) {
    ABinderProcess_setThreadPoolMaxThreadCount(0);

    // Strong reference to MyFoo kept by service manager.
@@ -225,16 +262,54 @@ TEST(NdkBinder, AddServiceMultipleTimes) {
    EXPECT_EQ(IFoo::getService(kInstanceName1), IFoo::getService(kInstanceName2));
}

TEST(NdkBinder, SentAidlBinderCanBeDestroyed) {
    static volatile bool destroyed = false;
    static std::mutex dMutex;
    static std::condition_variable cv;

    class MyEmpty : public aidl::BnEmpty {
        virtual ~MyEmpty() {
            destroyed = true;
            cv.notify_one();
        }
    };

    std::shared_ptr<MyEmpty> empty = ndk::SharedRefBase::make<MyEmpty>();

    ndk::SpAIBinder binder(AServiceManager_getService(kBinderNdkUnitTestService));
    std::shared_ptr<aidl::IBinderNdkUnitTest> service =
            aidl::IBinderNdkUnitTest::fromBinder(binder);

    EXPECT_FALSE(destroyed);

    service->takeInterface(empty);
    service->forceFlushCommands();
    empty = nullptr;

    // give other binder thread time to process commands
    {
        using namespace std::chrono_literals;
        std::unique_lock<std::mutex> lk(dMutex);
        cv.wait_for(lk, 1s, [] { return destroyed; });
    }

    EXPECT_TRUE(destroyed);
}

int main(int argc, char* argv[]) {
    ::testing::InitGoogleTest(&argc, argv);

    if (fork() == 0) {
        prctl(PR_SET_PDEATHSIG, SIGHUP);
        return service(IFoo::kInstanceNameToDieFor);
        return manualService(IFoo::kInstanceNameToDieFor);
    }
    if (fork() == 0) {
        prctl(PR_SET_PDEATHSIG, SIGHUP);
        return manualService(IFoo::kSomeInstanceName);
    }
    if (fork() == 0) {
        prctl(PR_SET_PDEATHSIG, SIGHUP);
        return service(IFoo::kSomeInstanceName);
        return generatedService();
    }

    ABinderProcess_setThreadPoolMaxThreadCount(1);  // to recieve death notifications/callbacks