/* * Copyright (C) 2018 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 #include #include #include #include #include #include #include #include // warning: this is assuming that libbinder_ndk is using the same copy // of libbinder that we are. #include #include #include #include #include #include #include #include using namespace android; constexpr char kExistingNonNdkService[] = "SurfaceFlinger"; constexpr char kBinderNdkUnitTestService[] = "BinderNdkUnitTest"; class MyBinderNdkUnitTest : public aidl::BnBinderNdkUnitTest { ndk::ScopedAStatus takeInterface(const std::shared_ptr& 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(); } binder_status_t handleShellCommand(int /*in*/, int out, int /*err*/, const char** args, uint32_t numArgs) override { for (uint32_t i = 0; i < numArgs; i++) { dprintf(out, "%s", args[i]); } fsync(out); return STATUS_OK; } }; int generatedService() { ABinderProcess_setThreadPoolMaxThreadCount(0); auto service = ndk::SharedRefBase::make(); 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; LOG(INFO) << "doubleNumber (" << in << ") => " << *out; return STATUS_OK; } binder_status_t die() override { LOG(FATAL) << "IFoo::die called!"; return STATUS_UNKNOWN_ERROR; } }; int manualService(const char* instance) { ABinderProcess_setThreadPoolMaxThreadCount(0); // Strong reference to MyFoo kept by service manager. binder_status_t status = (new MyFoo)->addService(instance); if (status != STATUS_OK) { LOG(FATAL) << "Could not register: " << status << " " << instance; } ABinderProcess_joinThreadPool(); return 1; // should not return } // This is too slow // TEST(NdkBinder, GetServiceThatDoesntExist) { // sp foo = IFoo::getService("asdfghkl;"); // EXPECT_EQ(nullptr, foo.get()); // } TEST(NdkBinder, CheckServiceThatDoesntExist) { AIBinder* binder = AServiceManager_checkService("asdfghkl;"); ASSERT_EQ(nullptr, binder); } TEST(NdkBinder, CheckServiceThatDoesExist) { AIBinder* binder = AServiceManager_checkService(kExistingNonNdkService); EXPECT_NE(nullptr, binder); EXPECT_EQ(STATUS_OK, AIBinder_ping(binder)); AIBinder_decStrong(binder); } TEST(NdkBinder, DoubleNumber) { sp foo = IFoo::getService(IFoo::kSomeInstanceName); ASSERT_NE(foo, nullptr); int32_t out; EXPECT_EQ(STATUS_OK, foo->doubleNumber(1, &out)); EXPECT_EQ(2, out); } void LambdaOnDeath(void* cookie) { auto onDeath = static_cast*>(cookie); (*onDeath)(); }; TEST(NdkBinder, DeathRecipient) { using namespace std::chrono_literals; AIBinder* binder; sp 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 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(&onDeath))); // the binder driver should return this if the service dies during the transaction EXPECT_EQ(STATUS_DEAD_OBJECT, foo->die()); foo = nullptr; std::unique_lock lock(deathMutex); EXPECT_TRUE(deathCv.wait_for(lock, 1s, [&] { return deathRecieved; })); EXPECT_TRUE(deathRecieved); AIBinder_DeathRecipient_delete(recipient); AIBinder_decStrong(binder); binder = nullptr; } TEST(NdkBinder, RetrieveNonNdkService) { AIBinder* binder = AServiceManager_getService(kExistingNonNdkService); ASSERT_NE(nullptr, binder); EXPECT_TRUE(AIBinder_isRemote(binder)); EXPECT_TRUE(AIBinder_isAlive(binder)); EXPECT_EQ(STATUS_OK, AIBinder_ping(binder)); AIBinder_decStrong(binder); } void OnBinderDeath(void* cookie) { LOG(ERROR) << "BINDER DIED. COOKIE: " << cookie; } TEST(NdkBinder, LinkToDeath) { AIBinder* binder = AServiceManager_getService(kExistingNonNdkService); ASSERT_NE(nullptr, binder); AIBinder_DeathRecipient* recipient = AIBinder_DeathRecipient_new(OnBinderDeath); ASSERT_NE(nullptr, recipient); EXPECT_EQ(STATUS_OK, AIBinder_linkToDeath(binder, recipient, nullptr)); EXPECT_EQ(STATUS_OK, AIBinder_linkToDeath(binder, recipient, nullptr)); EXPECT_EQ(STATUS_OK, AIBinder_unlinkToDeath(binder, recipient, nullptr)); EXPECT_EQ(STATUS_OK, AIBinder_unlinkToDeath(binder, recipient, nullptr)); EXPECT_EQ(STATUS_NAME_NOT_FOUND, AIBinder_unlinkToDeath(binder, recipient, nullptr)); AIBinder_DeathRecipient_delete(recipient); AIBinder_decStrong(binder); } class MyTestFoo : public IFoo { 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; } }; TEST(NdkBinder, GetServiceInProcess) { static const char* kInstanceName = "test-get-service-in-process"; sp foo = new MyTestFoo; EXPECT_EQ(STATUS_OK, foo->addService(kInstanceName)); sp getFoo = IFoo::getService(kInstanceName); EXPECT_EQ(foo.get(), getFoo.get()); int32_t out; EXPECT_EQ(STATUS_OK, getFoo->doubleNumber(1, &out)); EXPECT_EQ(2, out); } TEST(NdkBinder, EqualityOfRemoteBinderPointer) { AIBinder* binderA = AServiceManager_getService(kExistingNonNdkService); ASSERT_NE(nullptr, binderA); AIBinder* binderB = AServiceManager_getService(kExistingNonNdkService); ASSERT_NE(nullptr, binderB); EXPECT_EQ(binderA, binderB); AIBinder_decStrong(binderA); AIBinder_decStrong(binderB); } TEST(NdkBinder, ToFromJavaNullptr) { EXPECT_EQ(nullptr, AIBinder_toJavaBinder(nullptr, nullptr)); EXPECT_EQ(nullptr, AIBinder_fromJavaBinder(nullptr, nullptr)); } TEST(NdkBinder, ABpBinderRefCount) { AIBinder* binder = AServiceManager_getService(kExistingNonNdkService); AIBinder_Weak* wBinder = AIBinder_Weak_new(binder); ASSERT_NE(nullptr, binder); EXPECT_EQ(1, AIBinder_debugGetRefCount(binder)); AIBinder_decStrong(binder); // assert because would need to decStrong if non-null and we shouldn't need to add a no-op here ASSERT_NE(nullptr, AIBinder_Weak_promote(wBinder)); AIBinder_Weak_delete(wBinder); } TEST(NdkBinder, AddServiceMultipleTimes) { static const char* kInstanceName1 = "test-multi-1"; static const char* kInstanceName2 = "test-multi-2"; sp foo = new MyTestFoo; EXPECT_EQ(STATUS_OK, foo->addService(kInstanceName1)); EXPECT_EQ(STATUS_OK, foo->addService(kInstanceName2)); 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 empty = ndk::SharedRefBase::make(); ndk::SpAIBinder binder(AServiceManager_getService(kBinderNdkUnitTestService)); std::shared_ptr 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 lk(dMutex); cv.wait_for(lk, 1s, [] { return destroyed; }); } EXPECT_TRUE(destroyed); } class MyResultReceiver : public BnResultReceiver { public: Mutex mMutex; Condition mCondition; bool mHaveResult = false; int32_t mResult = 0; virtual void send(int32_t resultCode) { AutoMutex _l(mMutex); mResult = resultCode; mHaveResult = true; mCondition.signal(); } int32_t waitForResult() { AutoMutex _l(mMutex); while (!mHaveResult) { mCondition.wait(mMutex); } return mResult; } }; class MyShellCallback : public BnShellCallback { public: virtual int openFile(const String16& /*path*/, const String16& /*seLinuxContext*/, const String16& /*mode*/) { // Empty implementation. return 0; } }; bool ReadFdToString(int fd, std::string* content) { char buf[64]; ssize_t n; while ((n = TEMP_FAILURE_RETRY(read(fd, &buf[0], sizeof(buf)))) > 0) { content->append(buf, n); } return (n == 0) ? true : false; } std::string shellCmdToString(sp unitTestService, const std::vector& args) { int inFd[2] = {-1, -1}; int outFd[2] = {-1, -1}; int errFd[2] = {-1, -1}; EXPECT_EQ(0, socketpair(AF_UNIX, SOCK_STREAM, 0, inFd)); EXPECT_EQ(0, socketpair(AF_UNIX, SOCK_STREAM, 0, outFd)); EXPECT_EQ(0, socketpair(AF_UNIX, SOCK_STREAM, 0, errFd)); sp cb = new MyShellCallback(); sp resultReceiver = new MyResultReceiver(); Vector argsVec; for (int i = 0; i < args.size(); i++) { argsVec.add(String16(args[i])); } status_t error = IBinder::shellCommand(unitTestService, inFd[0], outFd[0], errFd[0], argsVec, cb, resultReceiver); EXPECT_EQ(error, android::OK); status_t res = resultReceiver->waitForResult(); EXPECT_EQ(res, android::OK); close(inFd[0]); close(inFd[1]); close(outFd[0]); close(errFd[0]); close(errFd[1]); std::string ret; EXPECT_TRUE(ReadFdToString(outFd[1], &ret)); close(outFd[1]); return ret; } TEST(NdkBinder, UseHandleShellCommand) { static const sp sm(android::defaultServiceManager()); sp testService = sm->getService(String16(kBinderNdkUnitTestService)); EXPECT_EQ("", shellCmdToString(testService, {})); EXPECT_EQ("", shellCmdToString(testService, {"", ""})); EXPECT_EQ("Hello world!", shellCmdToString(testService, {"Hello ", "world!"})); EXPECT_EQ("CMD", shellCmdToString(testService, {"C", "M", "D"})); } int main(int argc, char* argv[]) { ::testing::InitGoogleTest(&argc, argv); if (fork() == 0) { prctl(PR_SET_PDEATHSIG, SIGHUP); 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 generatedService(); } ABinderProcess_setThreadPoolMaxThreadCount(1); // to recieve death notifications/callbacks ABinderProcess_startThreadPool(); return RUN_ALL_TESTS(); } #include #include #include