/* * 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 #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 #include #include "android/binder_ibinder.h" using namespace android; constexpr char kExistingNonNdkService[] = "SurfaceFlinger"; constexpr char kBinderNdkUnitTestService[] = "BinderNdkUnitTest"; constexpr char kLazyBinderNdkUnitTestService[] = "LazyBinderNdkUnitTest"; constexpr char kForcePersistNdkUnitTestService[] = "ForcePersistNdkUnitTestService"; constexpr char kActiveServicesNdkUnitTestService[] = "ActiveServicesNdkUnitTestService"; constexpr unsigned int kShutdownWaitTime = 10; constexpr uint64_t kContextTestValue = 0xb4e42fb4d9a1d715; class MyBinderNdkUnitTest : public aidl::BnBinderNdkUnitTest { ndk::ScopedAStatus repeatInt(int32_t in, int32_t* out) { *out = in; return ndk::ScopedAStatus::ok(); } 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(); } ndk::ScopedAStatus getsRequestedSid(bool* out) { const char* sid = AIBinder_getCallingSid(); std::cout << "Got security context: " << (sid ?: "null") << std::endl; *out = sid != nullptr; 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; } ndk::ScopedAStatus forcePersist(bool persist) { AServiceManager_forceLazyServicesPersist(persist); return ndk::ScopedAStatus::ok(); } ndk::ScopedAStatus setCustomActiveServicesCallback() { AServiceManager_setActiveServicesCallback(activeServicesCallback, this); return ndk::ScopedAStatus::ok(); } static bool activeServicesCallback(bool hasClients, void* context) { if (hasClients) { return false; } // Unregister all services if (!AServiceManager_tryUnregister()) { // Prevent shutdown (test will fail) return false; } // Re-register all services AServiceManager_reRegister(); // Unregister again before shutdown if (!AServiceManager_tryUnregister()) { // Prevent shutdown (test will fail) return false; } // Check if the context was passed correctly MyBinderNdkUnitTest* service = static_cast(context); if (service->contextTestValue != kContextTestValue) { // Prevent shutdown (test will fail) return false; } exit(EXIT_SUCCESS); // Unreachable } uint64_t contextTestValue = kContextTestValue; }; int generatedService() { ABinderProcess_setThreadPoolMaxThreadCount(0); auto service = ndk::SharedRefBase::make(); auto binder = service->asBinder(); AIBinder_setRequestingSid(binder.get(), true); binder_exception_t exception = AServiceManager_addService(binder.get(), kBinderNdkUnitTestService); if (exception != EX_NONE) { LOG(FATAL) << "Could not register: " << exception << " " << 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; } }; void manualService(const char* instance) { // Strong reference to MyFoo kept by service manager. binder_exception_t exception = (new MyFoo)->addService(instance); if (exception != EX_NONE) { LOG(FATAL) << "Could not register: " << exception << " " << instance; } } int manualPollingService(const char* instance) { int fd; CHECK(STATUS_OK == ABinderProcess_setupPolling(&fd)); manualService(instance); class Handler : public LooperCallback { int handleEvent(int /*fd*/, int /*events*/, void* /*data*/) override { ABinderProcess_handlePolledCommands(); return 1; // Continue receiving callbacks. } }; sp looper = Looper::prepare(0 /* opts */); looper->addFd(fd, Looper::POLL_CALLBACK, Looper::EVENT_INPUT, new Handler(), nullptr /*data*/); // normally, would add additional fds while (true) { looper->pollAll(-1 /* timeoutMillis */); } return 1; // should not reach } int manualThreadPoolService(const char* instance) { ABinderProcess_setThreadPoolMaxThreadCount(0); manualService(instance); ABinderProcess_joinThreadPool(); return 1; } int lazyService(const char* instance) { ABinderProcess_setThreadPoolMaxThreadCount(0); // Wait to register this service to make sure the main test process will // actually wait for the service to be available. Tested with sleep(60), // and reduced for sake of time. sleep(1); // Strong reference to MyBinderNdkUnitTest kept by service manager. // This is just for testing, it has no corresponding init behavior. auto service = ndk::SharedRefBase::make(); auto binder = service->asBinder(); binder_status_t status = AServiceManager_registerLazyService(binder.get(), instance); if (status != STATUS_OK) { LOG(FATAL) << "Could not register: " << status << " " << instance; } ABinderProcess_joinThreadPool(); return 1; // should not return } bool isServiceRunning(const char* serviceName) { AIBinder* binder = AServiceManager_checkService(serviceName); if (binder == nullptr) { return false; } AIBinder_decStrong(binder); return true; } 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, UnimplementedDump) { sp foo = IFoo::getService(IFoo::kSomeInstanceName); ASSERT_NE(foo, nullptr); AIBinder* binder = foo->getBinder(); EXPECT_EQ(OK, AIBinder_dump(binder, STDOUT_FILENO, nullptr, 0)); AIBinder_decStrong(binder); } TEST(NdkBinder, UnimplementedShell) { // libbinder_ndk doesn't support calling shell, so we are calling from the // libbinder across processes to the NDK service which doesn't implement // shell static const sp sm(android::defaultServiceManager()); sp testService = sm->getService(String16(IFoo::kSomeInstanceName)); Vector argsVec; EXPECT_EQ(OK, IBinder::shellCommand(testService, STDIN_FILENO, STDOUT_FILENO, STDERR_FILENO, argsVec, nullptr, nullptr)); } 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 defaultInstanceCounter(const char* instance, void* context) { if (strcmp(instance, "default") == 0) { ++*(size_t*)(context); } } TEST(NdkBinder, GetDeclaredInstances) { bool hasLight = AServiceManager_isDeclared("android.hardware.light.ILights/default"); size_t count; AServiceManager_forEachDeclaredInstance("android.hardware.light.ILights", &count, defaultInstanceCounter); // At the time of writing this test, there is no good interface guaranteed // to be on all devices. Cuttlefish has light, so this will generally test // things. EXPECT_EQ(count, hasLight ? 1 : 0); } TEST(NdkBinder, GetLazyService) { // Not declared in the vintf manifest ASSERT_FALSE(AServiceManager_isDeclared(kLazyBinderNdkUnitTestService)); ndk::SpAIBinder binder(AServiceManager_waitForService(kLazyBinderNdkUnitTestService)); std::shared_ptr service = aidl::IBinderNdkUnitTest::fromBinder(binder); ASSERT_NE(service, nullptr); EXPECT_EQ(STATUS_OK, AIBinder_ping(binder.get())); } TEST(NdkBinder, IsUpdatable) { bool isUpdatable = AServiceManager_isUpdatableViaApex("android.hardware.light.ILights/default"); EXPECT_EQ(isUpdatable, false); } // This is too slow TEST(NdkBinder, CheckLazyServiceShutDown) { ndk::SpAIBinder binder(AServiceManager_waitForService(kLazyBinderNdkUnitTestService)); std::shared_ptr service = aidl::IBinderNdkUnitTest::fromBinder(binder); ASSERT_NE(service, nullptr); EXPECT_EQ(STATUS_OK, AIBinder_ping(binder.get())); binder = nullptr; service = nullptr; IPCThreadState::self()->flushCommands(); // Make sure the service is dead after some time of no use sleep(kShutdownWaitTime); ASSERT_EQ(nullptr, AServiceManager_checkService(kLazyBinderNdkUnitTestService)); } TEST(NdkBinder, ForcedPersistenceTest) { for (int i = 0; i < 2; i++) { ndk::SpAIBinder binder(AServiceManager_waitForService(kForcePersistNdkUnitTestService)); std::shared_ptr service = aidl::IBinderNdkUnitTest::fromBinder(binder); ASSERT_NE(service, nullptr); ASSERT_TRUE(service->forcePersist(i == 0).isOk()); binder = nullptr; service = nullptr; IPCThreadState::self()->flushCommands(); sleep(kShutdownWaitTime); bool isRunning = isServiceRunning(kForcePersistNdkUnitTestService); if (i == 0) { ASSERT_TRUE(isRunning) << "Service shut down when it shouldn't have."; } else { ASSERT_FALSE(isRunning) << "Service failed to shut down."; } } } TEST(NdkBinder, ActiveServicesCallbackTest) { ndk::SpAIBinder binder(AServiceManager_waitForService(kActiveServicesNdkUnitTestService)); std::shared_ptr service = aidl::IBinderNdkUnitTest::fromBinder(binder); ASSERT_NE(service, nullptr); ASSERT_TRUE(service->setCustomActiveServicesCallback().isOk()); binder = nullptr; service = nullptr; IPCThreadState::self()->flushCommands(); sleep(kShutdownWaitTime); ASSERT_FALSE(isServiceRunning(kActiveServicesNdkUnitTestService)) << "Service failed to shut down."; } 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, AddNullService) { EXPECT_EQ(EX_ILLEGAL_ARGUMENT, AServiceManager_addService(nullptr, "any-service-name")); } TEST(NdkBinder, AddInvalidServiceName) { sp foo = new MyTestFoo; EXPECT_EQ(EX_ILLEGAL_ARGUMENT, foo->addService("!@#$%^&")); } TEST(NdkBinder, GetServiceInProcess) { static const char* kInstanceName = "test-get-service-in-process"; sp foo = new MyTestFoo; EXPECT_EQ(EX_NONE, 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_EQ(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(EX_NONE, foo->addService(kInstanceName1)); EXPECT_EQ(EX_NONE, foo->addService(kInstanceName2)); EXPECT_EQ(IFoo::getService(kInstanceName1), IFoo::getService(kInstanceName2)); } TEST(NdkBinder, RequestedSidWorks) { ndk::SpAIBinder binder(AServiceManager_getService(kBinderNdkUnitTestService)); std::shared_ptr service = aidl::IBinderNdkUnitTest::fromBinder(binder); bool gotSid = false; EXPECT_TRUE(service->getsRequestedSid(&gotSid).isOk()); EXPECT_TRUE(gotSid); } 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); } TEST(NdkBinder, ConvertToPlatformBinder) { for (const ndk::SpAIBinder& binder : {// remote ndk::SpAIBinder(AServiceManager_getService(kBinderNdkUnitTestService)), // local ndk::SharedRefBase::make()->asBinder()}) { // convert to platform binder EXPECT_NE(binder.get(), nullptr); sp platformBinder = AIBinder_toPlatformBinder(binder.get()); EXPECT_NE(platformBinder.get(), nullptr); auto proxy = interface_cast(platformBinder); EXPECT_NE(proxy, nullptr); // use platform binder int out; EXPECT_TRUE(proxy->repeatInt(4, &out).isOk()); EXPECT_EQ(out, 4); // convert back ndk::SpAIBinder backBinder = ndk::SpAIBinder(AIBinder_fromPlatformBinder(platformBinder)); EXPECT_EQ(backBinder.get(), binder.get()); } } 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"})); } TEST(NdkBinder, GetClassInterfaceDescriptor) { ASSERT_STREQ(IFoo::kIFooDescriptor, AIBinder_Class_getDescriptor(IFoo::kClass)); } int main(int argc, char* argv[]) { ::testing::InitGoogleTest(&argc, argv); if (fork() == 0) { prctl(PR_SET_PDEATHSIG, SIGHUP); return manualThreadPoolService(IFoo::kInstanceNameToDieFor); } if (fork() == 0) { prctl(PR_SET_PDEATHSIG, SIGHUP); return manualPollingService(IFoo::kSomeInstanceName); } if (fork() == 0) { prctl(PR_SET_PDEATHSIG, SIGHUP); return lazyService(kLazyBinderNdkUnitTestService); } if (fork() == 0) { prctl(PR_SET_PDEATHSIG, SIGHUP); return lazyService(kForcePersistNdkUnitTestService); } if (fork() == 0) { prctl(PR_SET_PDEATHSIG, SIGHUP); return lazyService(kActiveServicesNdkUnitTestService); } if (fork() == 0) { prctl(PR_SET_PDEATHSIG, SIGHUP); return generatedService(); } ABinderProcess_setThreadPoolMaxThreadCount(1); // to receive death notifications/callbacks ABinderProcess_startThreadPool(); return RUN_ALL_TESTS(); } #include #include #include