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

Commit 3964935f authored by Steven Moreland's avatar Steven Moreland Committed by Gerrit Code Review
Browse files

Merge "libbinder_ndk: integration test for linkToDeath"

parents 9532aa99 507547fc
Loading
Loading
Loading
Loading
+9 −7
Original line number Diff line number Diff line
@@ -23,11 +23,12 @@ set -ex

function run_libbinder_ndk_test() {
    adb shell /data/nativetest64/libbinder_ndk_test_server/libbinder_ndk_test_server &
	local pid=$!
	trap "kill $pid" ERR
	adb shell /data/nativetest64/libbinder_ndk_test_client/libbinder_ndk_test_client
	trap '' ERR
	kill $pid

    # avoid getService 1s delay for most runs, non-critical
    sleep 0.1

    adb shell /data/nativetest64/libbinder_ndk_test_client/libbinder_ndk_test_client; \
        adb shell killall libbinder_ndk_test_server
}

[ "$1" != "--skip-build" ] && $ANDROID_BUILD_TOP/build/soong/soong_ui.bash --make-mode \
@@ -40,4 +41,5 @@ adb sync data
# very simple unit tests, tests things outside of the NDK as well
run_libbinder_ndk_test

atest android.binder.cts.NdkBinderTest
# CTS tests (much more comprehensive, new tests should ideally go here)
atest android.binder.cts
+3 −3
Original line number Diff line number Diff line
@@ -14,9 +14,6 @@
 * limitations under the License.
 */

// This test is a unit test of the low-level API that is presented here.
// Actual users should use AIDL to generate these complicated stubs.

cc_defaults {
    name: "test_libbinder_ndk_defaults",
    shared_libs: [
@@ -55,6 +52,9 @@ cc_defaults {
    ],
}

// This test is a unit test of the low-level API that is presented here,
// specifically the parts which are outside of the NDK. Actual users should
// also instead use AIDL to generate these stubs. See android.binder.cts.
cc_test {
    name: "libbinder_ndk_test_client",
    defaults: ["test_libbinder_ndk_test_defaults"],
+40 −13
Original line number Diff line number Diff line
@@ -18,10 +18,13 @@
#include <android/binder_manager.h>
#include <iface/iface.h>

#include <android/binder_auto_utils.h>

using ::android::sp;
using ::android::wp;

const char* IFoo::kSomeInstanceName = "libbinder_ndk-test-IFoo";
const char* IFoo::kInstanceNameToDieFor = "libbinder_ndk-test-IFoo-to-die";
const char* kIFooDescriptor = "my-special-IFoo-class";

struct IFoo_Class_Data {
@@ -49,12 +52,18 @@ binder_status_t IFoo_Class_onTransact(AIBinder* binder, transaction_code_t code,
    switch (code) {
        case IFoo::DOFOO: {
            int32_t valueIn;
            int32_t valueOut;
            stat = AParcel_readInt32(in, &valueIn);
            if (stat != STATUS_OK) break;
            int32_t valueOut = foo->doubleNumber(valueIn);
            stat = foo->doubleNumber(valueIn, &valueOut);
            if (stat != STATUS_OK) break;
            stat = AParcel_writeInt32(out, valueOut);
            break;
        }
        case IFoo::DIE: {
            stat = foo->die();
            break;
        }
    }

    return stat;
@@ -68,22 +77,36 @@ class BpFoo : public IFoo {
    BpFoo(AIBinder* binder) : mBinder(binder) {}
    virtual ~BpFoo() { AIBinder_decStrong(mBinder); }

    virtual int32_t doubleNumber(int32_t in) {
    virtual binder_status_t doubleNumber(int32_t in, int32_t* out) {
        binder_status_t stat = STATUS_OK;

        AParcel* parcelIn;
        CHECK(STATUS_OK == AIBinder_prepareTransaction(mBinder, &parcelIn));
        stat = AIBinder_prepareTransaction(mBinder, &parcelIn);
        if (stat != STATUS_OK) return stat;

        stat = AParcel_writeInt32(parcelIn, in);
        if (stat != STATUS_OK) return stat;

        ::ndk::ScopedAParcel parcelOut;
        stat = AIBinder_transact(mBinder, IFoo::DOFOO, &parcelIn, parcelOut.getR(), 0 /*flags*/);
        if (stat != STATUS_OK) return stat;

        CHECK(STATUS_OK == AParcel_writeInt32(parcelIn, in));
        stat = AParcel_readInt32(parcelOut.get(), out);
        if (stat != STATUS_OK) return stat;

        AParcel* parcelOut;
        CHECK(STATUS_OK ==
              AIBinder_transact(mBinder, IFoo::DOFOO, &parcelIn, &parcelOut, 0 /*flags*/));
        return stat;
    }

        int32_t out;
        CHECK(STATUS_OK == AParcel_readInt32(parcelOut, &out));
    virtual binder_status_t die() {
        binder_status_t stat = STATUS_OK;

        AParcel* parcelIn;
        stat = AIBinder_prepareTransaction(mBinder, &parcelIn);

        AParcel_delete(parcelOut);
        ::ndk::ScopedAParcel parcelOut;
        stat = AIBinder_transact(mBinder, IFoo::DIE, &parcelIn, parcelOut.getR(), 0 /*flags*/);

        return out;
        return stat;
    }

   private:
@@ -117,7 +140,7 @@ binder_status_t IFoo::addService(const char* instance) {
    return status;
}

sp<IFoo> IFoo::getService(const char* instance) {
sp<IFoo> IFoo::getService(const char* instance, AIBinder** outBinder) {
    AIBinder* binder = AServiceManager_getService(instance);  // maybe nullptr
    if (binder == nullptr) {
        return nullptr;
@@ -128,6 +151,11 @@ sp<IFoo> IFoo::getService(const char* instance) {
        return nullptr;
    }

    if (outBinder != nullptr) {
        AIBinder_incStrong(binder);
        *outBinder = binder;
    }

    if (AIBinder_isRemote(binder)) {
        sp<IFoo> ret = new BpFoo(binder);  // takes ownership of binder
        return ret;
@@ -143,7 +171,6 @@ sp<IFoo> IFoo::getService(const char* instance) {
    CHECK(held == binder);
    AIBinder_decStrong(held);

    // IFoo only keeps a weak reference to AIBinder, so we can drop this
    AIBinder_decStrong(binder);
    return ret;
}
+14 −3
Original line number Diff line number Diff line
@@ -19,22 +19,33 @@
#include <android/binder_ibinder.h>
#include <utils/RefBase.h>

// warning: it is recommended to use AIDL output instead of this. binder_ibinder_utils.h and some of
// the other niceties make sure that, for instance, binder proxies are always the same. They also
// don't use internal Android APIs like refbase which are used here only for convenience.

class IFoo : public virtual ::android::RefBase {
   public:
    static const char* kSomeInstanceName;
    static const char* kInstanceNameToDieFor;

    static AIBinder_Class* kClass;

    // Takes ownership of IFoo
    binder_status_t addService(const char* instance);
    static ::android::sp<IFoo> getService(const char* instance);
    static ::android::sp<IFoo> getService(const char* instance, AIBinder** outBinder = nullptr);

    enum Call {
        DOFOO = FIRST_CALL_TRANSACTION + 0,
        DIE = FIRST_CALL_TRANSACTION + 1,
    };

    virtual ~IFoo();
    virtual int32_t doubleNumber(int32_t in) = 0;

    virtual binder_status_t doubleNumber(int32_t in, int32_t* out) = 0;
    virtual binder_status_t die() = 0;

   private:
    AIBinder_Weak* mWeakBinder = nullptr;  // maybe owns AIBinder
    // this variable is only when IFoo is local (since this test combines 'IFoo' and 'BnFoo'), not
    // for BpFoo.
    AIBinder_Weak* mWeakBinder = nullptr;
};
+65 −8
Original line number Diff line number Diff line
@@ -21,6 +21,10 @@
#include <gtest/gtest.h>
#include <iface/iface.h>

#include <chrono>
#include <condition_variable>
#include <mutex>

using ::android::sp;

constexpr char kExistingNonNdkService[] = "SurfaceFlinger";
@@ -34,7 +38,47 @@ constexpr char kExistingNonNdkService[] = "SurfaceFlinger";
TEST(NdkBinder, DoubleNumber) {
    sp<IFoo> foo = IFoo::getService(IFoo::kSomeInstanceName);
    ASSERT_NE(foo, nullptr);
    EXPECT_EQ(2, foo->doubleNumber(1));

    int32_t out;
    EXPECT_EQ(STATUS_OK, foo->doubleNumber(1, &out));
    EXPECT_EQ(2, out);
}

void LambdaOnDeath(void* cookie) {
    auto onDeath = static_cast<std::function<void(void)>*>(cookie);
    (*onDeath)();
};
TEST(NdkBinder, DeathRecipient) {
    using namespace std::chrono_literals;

    AIBinder* binder;
    sp<IFoo> foo = IFoo::getService(IFoo::kInstanceNameToDieFor, &binder);
    ASSERT_NE(nullptr, foo.get());
    ASSERT_NE(nullptr, binder);

    std::mutex deathMutex;
    std::condition_variable deathCv;
    bool deathRecieved = false;

    std::function<void(void)> onDeath = [&] {
        std::cerr << "Binder died (as requested)." << std::endl;
        deathRecieved = true;
        deathCv.notify_one();
    };

    AIBinder_DeathRecipient* recipient = AIBinder_DeathRecipient_new(LambdaOnDeath);

    EXPECT_EQ(STATUS_OK, AIBinder_linkToDeath(binder, recipient, static_cast<void*>(&onDeath)));

    // the binder driver should return this if the service dies during the transaction
    EXPECT_EQ(STATUS_DEAD_OBJECT, foo->die());

    std::unique_lock<std::mutex> lock(deathMutex);
    EXPECT_TRUE(deathCv.wait_for(lock, 1s, [&] { return deathRecieved; }));
    EXPECT_TRUE(deathRecieved);

    AIBinder_DeathRecipient_delete(recipient);
    AIBinder_decStrong(binder);
}

TEST(NdkBinder, RetrieveNonNdkService) {
@@ -52,9 +96,6 @@ void OnBinderDeath(void* cookie) {
}

TEST(NdkBinder, LinkToDeath) {
    ABinderProcess_setThreadPoolMaxThreadCount(1);  // to recieve death notifications
    ABinderProcess_startThreadPool();

    AIBinder* binder = AServiceManager_getService(kExistingNonNdkService);
    ASSERT_NE(nullptr, binder);

@@ -72,9 +113,14 @@ TEST(NdkBinder, LinkToDeath) {
}

class MyTestFoo : public IFoo {
    int32_t doubleNumber(int32_t in) override {
        LOG(INFO) << "doubleNumber " << in;
        return 2 * in;
    binder_status_t doubleNumber(int32_t in, int32_t* out) override {
        *out = 2 * in;
        LOG(INFO) << "doubleNumber (" << in << ") => " << *out;
        return STATUS_OK;
    }
    binder_status_t die() override {
        ADD_FAILURE() << "die called on local instance";
        return STATUS_OK;
    }
};

@@ -87,7 +133,9 @@ TEST(NdkBinder, GetServiceInProcess) {
    sp<IFoo> getFoo = IFoo::getService(kInstanceName);
    EXPECT_EQ(foo.get(), getFoo.get());

    EXPECT_EQ(2, getFoo->doubleNumber(1));
    int32_t out;
    EXPECT_EQ(STATUS_OK, getFoo->doubleNumber(1, &out));
    EXPECT_EQ(2, out);
}

TEST(NdkBinder, EqualityOfRemoteBinderPointer) {
@@ -132,6 +180,15 @@ TEST(NdkBinder, AddServiceMultipleTimes) {
    EXPECT_EQ(IFoo::getService(kInstanceName1), IFoo::getService(kInstanceName2));
}

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

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

    return RUN_ALL_TESTS();
}

#include <android/binder_auto_utils.h>
#include <android/binder_interface_utils.h>
#include <android/binder_parcel_utils.h>
Loading