| /* |
| * Copyright (C) 2014 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 <errno.h> |
| #include <fcntl.h> |
| #include <poll.h> |
| #include <pthread.h> |
| #include <stdio.h> |
| #include <stdlib.h> |
| |
| #include <gtest/gtest.h> |
| |
| #include <binder/Binder.h> |
| #include <binder/IBinder.h> |
| #include <binder/IPCThreadState.h> |
| #include <binder/IServiceManager.h> |
| |
| #define ARRAY_SIZE(array) (sizeof array / sizeof array[0]) |
| |
| using namespace android; |
| |
| static testing::Environment* binder_env; |
| static char *binderservername; |
| static char *binderserversuffix; |
| static char binderserverarg[] = "--binderserver"; |
| |
| static String16 binderLibTestServiceName = String16("test.binderLib"); |
| |
| enum BinderLibTestTranscationCode { |
| BINDER_LIB_TEST_NOP_TRANSACTION = IBinder::FIRST_CALL_TRANSACTION, |
| BINDER_LIB_TEST_REGISTER_SERVER, |
| BINDER_LIB_TEST_ADD_SERVER, |
| BINDER_LIB_TEST_CALL_BACK, |
| BINDER_LIB_TEST_NOP_CALL_BACK, |
| BINDER_LIB_TEST_GET_SELF_TRANSACTION, |
| BINDER_LIB_TEST_GET_ID_TRANSACTION, |
| BINDER_LIB_TEST_INDIRECT_TRANSACTION, |
| BINDER_LIB_TEST_SET_ERROR_TRANSACTION, |
| BINDER_LIB_TEST_GET_STATUS_TRANSACTION, |
| BINDER_LIB_TEST_ADD_STRONG_REF_TRANSACTION, |
| BINDER_LIB_TEST_LINK_DEATH_TRANSACTION, |
| BINDER_LIB_TEST_WRITE_FILE_TRANSACTION, |
| BINDER_LIB_TEST_PROMOTE_WEAK_REF_TRANSACTION, |
| BINDER_LIB_TEST_EXIT_TRANSACTION, |
| BINDER_LIB_TEST_DELAYED_EXIT_TRANSACTION, |
| BINDER_LIB_TEST_GET_PTR_SIZE_TRANSACTION, |
| BINDER_LIB_TEST_CREATE_BINDER_TRANSACTION, |
| }; |
| |
| pid_t start_server_process(int arg2) |
| { |
| int ret; |
| pid_t pid; |
| status_t status; |
| int pipefd[2]; |
| char stri[16]; |
| char strpipefd1[16]; |
| char *childargv[] = { |
| binderservername, |
| binderserverarg, |
| stri, |
| strpipefd1, |
| binderserversuffix, |
| NULL |
| }; |
| |
| ret = pipe(pipefd); |
| if (ret < 0) |
| return ret; |
| |
| snprintf(stri, sizeof(stri), "%d", arg2); |
| snprintf(strpipefd1, sizeof(strpipefd1), "%d", pipefd[1]); |
| |
| pid = fork(); |
| if (pid == -1) |
| return pid; |
| if (pid == 0) { |
| close(pipefd[0]); |
| execv(binderservername, childargv); |
| status = -errno; |
| write(pipefd[1], &status, sizeof(status)); |
| fprintf(stderr, "execv failed, %s\n", strerror(errno)); |
| _exit(EXIT_FAILURE); |
| } |
| close(pipefd[1]); |
| ret = read(pipefd[0], &status, sizeof(status)); |
| //printf("pipe read returned %d, status %d\n", ret, status); |
| close(pipefd[0]); |
| if (ret == sizeof(status)) { |
| ret = status; |
| } else { |
| kill(pid, SIGKILL); |
| if (ret >= 0) { |
| ret = NO_INIT; |
| } |
| } |
| if (ret < 0) { |
| wait(NULL); |
| return ret; |
| } |
| return pid; |
| } |
| |
| class BinderLibTestEnv : public ::testing::Environment { |
| public: |
| BinderLibTestEnv() {} |
| sp<IBinder> getServer(void) { |
| return m_server; |
| } |
| |
| private: |
| virtual void SetUp() { |
| m_serverpid = start_server_process(0); |
| //printf("m_serverpid %d\n", m_serverpid); |
| ASSERT_GT(m_serverpid, 0); |
| |
| sp<IServiceManager> sm = defaultServiceManager(); |
| //printf("%s: pid %d, get service\n", __func__, m_pid); |
| m_server = sm->getService(binderLibTestServiceName); |
| ASSERT_TRUE(m_server != NULL); |
| //printf("%s: pid %d, get service done\n", __func__, m_pid); |
| } |
| virtual void TearDown() { |
| status_t ret; |
| Parcel data, reply; |
| int exitStatus; |
| pid_t pid; |
| |
| //printf("%s: pid %d\n", __func__, m_pid); |
| if (m_server != NULL) { |
| ret = m_server->transact(BINDER_LIB_TEST_GET_STATUS_TRANSACTION, data, &reply); |
| EXPECT_EQ(0, ret); |
| ret = m_server->transact(BINDER_LIB_TEST_EXIT_TRANSACTION, data, &reply, TF_ONE_WAY); |
| EXPECT_EQ(0, ret); |
| } |
| if (m_serverpid > 0) { |
| //printf("wait for %d\n", m_pids[i]); |
| pid = wait(&exitStatus); |
| EXPECT_EQ(m_serverpid, pid); |
| EXPECT_TRUE(WIFEXITED(exitStatus)); |
| EXPECT_EQ(0, WEXITSTATUS(exitStatus)); |
| } |
| } |
| |
| pid_t m_serverpid; |
| sp<IBinder> m_server; |
| }; |
| |
| class BinderLibTest : public ::testing::Test { |
| public: |
| virtual void SetUp() { |
| m_server = static_cast<BinderLibTestEnv *>(binder_env)->getServer(); |
| } |
| virtual void TearDown() { |
| } |
| protected: |
| sp<IBinder> addServer(int32_t *idPtr = NULL) |
| { |
| int ret; |
| int32_t id; |
| Parcel data, reply; |
| sp<IBinder> binder; |
| |
| ret = m_server->transact(BINDER_LIB_TEST_ADD_SERVER, data, &reply); |
| EXPECT_EQ(NO_ERROR, ret); |
| |
| EXPECT_FALSE(binder != NULL); |
| binder = reply.readStrongBinder(); |
| EXPECT_TRUE(binder != NULL); |
| ret = reply.readInt32(&id); |
| EXPECT_EQ(NO_ERROR, ret); |
| if (idPtr) |
| *idPtr = id; |
| return binder; |
| } |
| void waitForReadData(int fd, int timeout_ms) { |
| int ret; |
| pollfd pfd = pollfd(); |
| |
| pfd.fd = fd; |
| pfd.events = POLLIN; |
| ret = poll(&pfd, 1, timeout_ms); |
| EXPECT_EQ(1, ret); |
| } |
| |
| sp<IBinder> m_server; |
| }; |
| |
| class BinderLibTestBundle : public Parcel |
| { |
| public: |
| BinderLibTestBundle(void) {} |
| BinderLibTestBundle(const Parcel *source) : m_isValid(false) { |
| int32_t mark; |
| int32_t bundleLen; |
| size_t pos; |
| |
| if (source->readInt32(&mark)) |
| return; |
| if (mark != MARK_START) |
| return; |
| if (source->readInt32(&bundleLen)) |
| return; |
| pos = source->dataPosition(); |
| if (Parcel::appendFrom(source, pos, bundleLen)) |
| return; |
| source->setDataPosition(pos + bundleLen); |
| if (source->readInt32(&mark)) |
| return; |
| if (mark != MARK_END) |
| return; |
| m_isValid = true; |
| setDataPosition(0); |
| } |
| void appendTo(Parcel *dest) { |
| dest->writeInt32(MARK_START); |
| dest->writeInt32(dataSize()); |
| dest->appendFrom(this, 0, dataSize()); |
| dest->writeInt32(MARK_END); |
| }; |
| bool isValid(void) { |
| return m_isValid; |
| } |
| private: |
| enum { |
| MARK_START = B_PACK_CHARS('B','T','B','S'), |
| MARK_END = B_PACK_CHARS('B','T','B','E'), |
| }; |
| bool m_isValid; |
| }; |
| |
| class BinderLibTestEvent |
| { |
| public: |
| BinderLibTestEvent(void) |
| : m_eventTriggered(false) |
| { |
| pthread_mutex_init(&m_waitMutex, NULL); |
| pthread_cond_init(&m_waitCond, NULL); |
| } |
| int waitEvent(int timeout_s) |
| { |
| int ret; |
| pthread_mutex_lock(&m_waitMutex); |
| if (!m_eventTriggered) { |
| struct timespec ts; |
| clock_gettime(CLOCK_REALTIME, &ts); |
| ts.tv_sec += timeout_s; |
| pthread_cond_timedwait(&m_waitCond, &m_waitMutex, &ts); |
| } |
| ret = m_eventTriggered ? NO_ERROR : TIMED_OUT; |
| pthread_mutex_unlock(&m_waitMutex); |
| return ret; |
| } |
| protected: |
| void triggerEvent(void) { |
| pthread_mutex_lock(&m_waitMutex); |
| pthread_cond_signal(&m_waitCond); |
| m_eventTriggered = true; |
| pthread_mutex_unlock(&m_waitMutex); |
| }; |
| private: |
| pthread_mutex_t m_waitMutex; |
| pthread_cond_t m_waitCond; |
| bool m_eventTriggered; |
| }; |
| |
| class BinderLibTestCallBack : public BBinder, public BinderLibTestEvent |
| { |
| public: |
| BinderLibTestCallBack() |
| : m_result(NOT_ENOUGH_DATA) |
| { |
| } |
| status_t getResult(void) |
| { |
| return m_result; |
| } |
| |
| private: |
| virtual status_t onTransact(uint32_t code, |
| const Parcel& data, Parcel* reply, |
| uint32_t flags = 0) |
| { |
| (void)reply; |
| (void)flags; |
| switch(code) { |
| case BINDER_LIB_TEST_CALL_BACK: |
| m_result = data.readInt32(); |
| triggerEvent(); |
| return NO_ERROR; |
| default: |
| return UNKNOWN_TRANSACTION; |
| } |
| } |
| |
| status_t m_result; |
| }; |
| |
| class TestDeathRecipient : public IBinder::DeathRecipient, public BinderLibTestEvent |
| { |
| private: |
| virtual void binderDied(const wp<IBinder>& who) { |
| (void)who; |
| triggerEvent(); |
| }; |
| }; |
| |
| TEST_F(BinderLibTest, NopTransaction) { |
| status_t ret; |
| Parcel data, reply; |
| ret = m_server->transact(BINDER_LIB_TEST_NOP_TRANSACTION, data, &reply); |
| EXPECT_EQ(NO_ERROR, ret); |
| } |
| |
| TEST_F(BinderLibTest, SetError) { |
| int32_t testValue[] = { 0, -123, 123 }; |
| for (size_t i = 0; i < ARRAY_SIZE(testValue); i++) { |
| status_t ret; |
| Parcel data, reply; |
| data.writeInt32(testValue[i]); |
| ret = m_server->transact(BINDER_LIB_TEST_SET_ERROR_TRANSACTION, data, &reply); |
| EXPECT_EQ(testValue[i], ret); |
| } |
| } |
| |
| TEST_F(BinderLibTest, GetId) { |
| status_t ret; |
| int32_t id; |
| Parcel data, reply; |
| ret = m_server->transact(BINDER_LIB_TEST_GET_ID_TRANSACTION, data, &reply); |
| EXPECT_EQ(NO_ERROR, ret); |
| ret = reply.readInt32(&id); |
| EXPECT_EQ(NO_ERROR, ret); |
| EXPECT_EQ(0, id); |
| } |
| |
| TEST_F(BinderLibTest, PtrSize) { |
| status_t ret; |
| int32_t ptrsize; |
| Parcel data, reply; |
| sp<IBinder> server = addServer(); |
| ASSERT_TRUE(server != NULL); |
| ret = server->transact(BINDER_LIB_TEST_GET_PTR_SIZE_TRANSACTION, data, &reply); |
| EXPECT_EQ(NO_ERROR, ret); |
| ret = reply.readInt32(&ptrsize); |
| EXPECT_EQ(NO_ERROR, ret); |
| RecordProperty("TestPtrSize", sizeof(void *)); |
| RecordProperty("ServerPtrSize", sizeof(void *)); |
| } |
| |
| TEST_F(BinderLibTest, IndirectGetId2) |
| { |
| status_t ret; |
| int32_t id; |
| int32_t count; |
| Parcel data, reply; |
| int32_t serverId[3]; |
| |
| data.writeInt32(ARRAY_SIZE(serverId)); |
| for (size_t i = 0; i < ARRAY_SIZE(serverId); i++) { |
| sp<IBinder> server; |
| BinderLibTestBundle datai; |
| |
| server = addServer(&serverId[i]); |
| ASSERT_TRUE(server != NULL); |
| data.writeStrongBinder(server); |
| data.writeInt32(BINDER_LIB_TEST_GET_ID_TRANSACTION); |
| datai.appendTo(&data); |
| } |
| |
| ret = m_server->transact(BINDER_LIB_TEST_INDIRECT_TRANSACTION, data, &reply); |
| ASSERT_EQ(NO_ERROR, ret); |
| |
| ret = reply.readInt32(&id); |
| ASSERT_EQ(NO_ERROR, ret); |
| EXPECT_EQ(0, id); |
| |
| ret = reply.readInt32(&count); |
| ASSERT_EQ(NO_ERROR, ret); |
| EXPECT_EQ(ARRAY_SIZE(serverId), (size_t)count); |
| |
| for (size_t i = 0; i < (size_t)count; i++) { |
| BinderLibTestBundle replyi(&reply); |
| EXPECT_TRUE(replyi.isValid()); |
| ret = replyi.readInt32(&id); |
| EXPECT_EQ(NO_ERROR, ret); |
| EXPECT_EQ(serverId[i], id); |
| EXPECT_EQ(replyi.dataSize(), replyi.dataPosition()); |
| } |
| |
| EXPECT_EQ(reply.dataSize(), reply.dataPosition()); |
| } |
| |
| TEST_F(BinderLibTest, IndirectGetId3) |
| { |
| status_t ret; |
| int32_t id; |
| int32_t count; |
| Parcel data, reply; |
| int32_t serverId[3]; |
| |
| data.writeInt32(ARRAY_SIZE(serverId)); |
| for (size_t i = 0; i < ARRAY_SIZE(serverId); i++) { |
| sp<IBinder> server; |
| BinderLibTestBundle datai; |
| BinderLibTestBundle datai2; |
| |
| server = addServer(&serverId[i]); |
| ASSERT_TRUE(server != NULL); |
| data.writeStrongBinder(server); |
| data.writeInt32(BINDER_LIB_TEST_INDIRECT_TRANSACTION); |
| |
| datai.writeInt32(1); |
| datai.writeStrongBinder(m_server); |
| datai.writeInt32(BINDER_LIB_TEST_GET_ID_TRANSACTION); |
| datai2.appendTo(&datai); |
| |
| datai.appendTo(&data); |
| } |
| |
| ret = m_server->transact(BINDER_LIB_TEST_INDIRECT_TRANSACTION, data, &reply); |
| ASSERT_EQ(NO_ERROR, ret); |
| |
| ret = reply.readInt32(&id); |
| ASSERT_EQ(NO_ERROR, ret); |
| EXPECT_EQ(0, id); |
| |
| ret = reply.readInt32(&count); |
| ASSERT_EQ(NO_ERROR, ret); |
| EXPECT_EQ(ARRAY_SIZE(serverId), (size_t)count); |
| |
| for (size_t i = 0; i < (size_t)count; i++) { |
| int32_t counti; |
| |
| BinderLibTestBundle replyi(&reply); |
| EXPECT_TRUE(replyi.isValid()); |
| ret = replyi.readInt32(&id); |
| EXPECT_EQ(NO_ERROR, ret); |
| EXPECT_EQ(serverId[i], id); |
| |
| ret = replyi.readInt32(&counti); |
| ASSERT_EQ(NO_ERROR, ret); |
| EXPECT_EQ(1, counti); |
| |
| BinderLibTestBundle replyi2(&replyi); |
| EXPECT_TRUE(replyi2.isValid()); |
| ret = replyi2.readInt32(&id); |
| EXPECT_EQ(NO_ERROR, ret); |
| EXPECT_EQ(0, id); |
| EXPECT_EQ(replyi2.dataSize(), replyi2.dataPosition()); |
| |
| EXPECT_EQ(replyi.dataSize(), replyi.dataPosition()); |
| } |
| |
| EXPECT_EQ(reply.dataSize(), reply.dataPosition()); |
| } |
| |
| TEST_F(BinderLibTest, CallBack) |
| { |
| status_t ret; |
| Parcel data, reply; |
| sp<BinderLibTestCallBack> callBack = new BinderLibTestCallBack(); |
| data.writeStrongBinder(callBack); |
| ret = m_server->transact(BINDER_LIB_TEST_NOP_CALL_BACK, data, &reply, TF_ONE_WAY); |
| EXPECT_EQ(NO_ERROR, ret); |
| ret = callBack->waitEvent(5); |
| EXPECT_EQ(NO_ERROR, ret); |
| ret = callBack->getResult(); |
| EXPECT_EQ(NO_ERROR, ret); |
| } |
| |
| TEST_F(BinderLibTest, AddServer) |
| { |
| sp<IBinder> server = addServer(); |
| ASSERT_TRUE(server != NULL); |
| } |
| |
| TEST_F(BinderLibTest, DeathNotificationNoRefs) |
| { |
| status_t ret; |
| |
| sp<TestDeathRecipient> testDeathRecipient = new TestDeathRecipient(); |
| |
| { |
| sp<IBinder> binder = addServer(); |
| ASSERT_TRUE(binder != NULL); |
| ret = binder->linkToDeath(testDeathRecipient); |
| EXPECT_EQ(NO_ERROR, ret); |
| } |
| IPCThreadState::self()->flushCommands(); |
| ret = testDeathRecipient->waitEvent(5); |
| EXPECT_EQ(NO_ERROR, ret); |
| #if 0 /* Is there an unlink api that does not require a strong reference? */ |
| ret = binder->unlinkToDeath(testDeathRecipient); |
| EXPECT_EQ(NO_ERROR, ret); |
| #endif |
| } |
| |
| TEST_F(BinderLibTest, DeathNotificationWeakRef) |
| { |
| status_t ret; |
| wp<IBinder> wbinder; |
| |
| sp<TestDeathRecipient> testDeathRecipient = new TestDeathRecipient(); |
| |
| { |
| sp<IBinder> binder = addServer(); |
| ASSERT_TRUE(binder != NULL); |
| ret = binder->linkToDeath(testDeathRecipient); |
| EXPECT_EQ(NO_ERROR, ret); |
| wbinder = binder; |
| } |
| IPCThreadState::self()->flushCommands(); |
| ret = testDeathRecipient->waitEvent(5); |
| EXPECT_EQ(NO_ERROR, ret); |
| #if 0 /* Is there an unlink api that does not require a strong reference? */ |
| ret = binder->unlinkToDeath(testDeathRecipient); |
| EXPECT_EQ(NO_ERROR, ret); |
| #endif |
| } |
| |
| TEST_F(BinderLibTest, DeathNotificationStrongRef) |
| { |
| status_t ret; |
| sp<IBinder> sbinder; |
| |
| sp<TestDeathRecipient> testDeathRecipient = new TestDeathRecipient(); |
| |
| { |
| sp<IBinder> binder = addServer(); |
| ASSERT_TRUE(binder != NULL); |
| ret = binder->linkToDeath(testDeathRecipient); |
| EXPECT_EQ(NO_ERROR, ret); |
| sbinder = binder; |
| } |
| { |
| Parcel data, reply; |
| ret = sbinder->transact(BINDER_LIB_TEST_EXIT_TRANSACTION, data, &reply, TF_ONE_WAY); |
| EXPECT_EQ(0, ret); |
| } |
| IPCThreadState::self()->flushCommands(); |
| ret = testDeathRecipient->waitEvent(5); |
| EXPECT_EQ(NO_ERROR, ret); |
| ret = sbinder->unlinkToDeath(testDeathRecipient); |
| EXPECT_EQ(DEAD_OBJECT, ret); |
| } |
| |
| TEST_F(BinderLibTest, DeathNotificationMultiple) |
| { |
| status_t ret; |
| const int clientcount = 2; |
| sp<IBinder> target; |
| sp<IBinder> linkedclient[clientcount]; |
| sp<BinderLibTestCallBack> callBack[clientcount]; |
| sp<IBinder> passiveclient[clientcount]; |
| |
| target = addServer(); |
| ASSERT_TRUE(target != NULL); |
| for (int i = 0; i < clientcount; i++) { |
| { |
| Parcel data, reply; |
| |
| linkedclient[i] = addServer(); |
| ASSERT_TRUE(linkedclient[i] != NULL); |
| callBack[i] = new BinderLibTestCallBack(); |
| data.writeStrongBinder(target); |
| data.writeStrongBinder(callBack[i]); |
| ret = linkedclient[i]->transact(BINDER_LIB_TEST_LINK_DEATH_TRANSACTION, data, &reply, TF_ONE_WAY); |
| EXPECT_EQ(NO_ERROR, ret); |
| } |
| { |
| Parcel data, reply; |
| |
| passiveclient[i] = addServer(); |
| ASSERT_TRUE(passiveclient[i] != NULL); |
| data.writeStrongBinder(target); |
| ret = passiveclient[i]->transact(BINDER_LIB_TEST_ADD_STRONG_REF_TRANSACTION, data, &reply, TF_ONE_WAY); |
| EXPECT_EQ(NO_ERROR, ret); |
| } |
| } |
| { |
| Parcel data, reply; |
| ret = target->transact(BINDER_LIB_TEST_EXIT_TRANSACTION, data, &reply, TF_ONE_WAY); |
| EXPECT_EQ(0, ret); |
| } |
| |
| for (int i = 0; i < clientcount; i++) { |
| ret = callBack[i]->waitEvent(5); |
| EXPECT_EQ(NO_ERROR, ret); |
| ret = callBack[i]->getResult(); |
| EXPECT_EQ(NO_ERROR, ret); |
| } |
| } |
| |
| TEST_F(BinderLibTest, PassFile) { |
| int ret; |
| int pipefd[2]; |
| uint8_t buf[1] = { 0 }; |
| uint8_t write_value = 123; |
| |
| ret = pipe2(pipefd, O_NONBLOCK); |
| ASSERT_EQ(0, ret); |
| |
| { |
| Parcel data, reply; |
| uint8_t writebuf[1] = { write_value }; |
| |
| ret = data.writeFileDescriptor(pipefd[1], true); |
| EXPECT_EQ(NO_ERROR, ret); |
| |
| ret = data.writeInt32(sizeof(writebuf)); |
| EXPECT_EQ(NO_ERROR, ret); |
| |
| ret = data.write(writebuf, sizeof(writebuf)); |
| EXPECT_EQ(NO_ERROR, ret); |
| |
| ret = m_server->transact(BINDER_LIB_TEST_WRITE_FILE_TRANSACTION, data, &reply); |
| EXPECT_EQ(NO_ERROR, ret); |
| } |
| |
| ret = read(pipefd[0], buf, sizeof(buf)); |
| EXPECT_EQ(sizeof(buf), (size_t)ret); |
| EXPECT_EQ(write_value, buf[0]); |
| |
| waitForReadData(pipefd[0], 5000); /* wait for other proccess to close pipe */ |
| |
| ret = read(pipefd[0], buf, sizeof(buf)); |
| EXPECT_EQ(0, ret); |
| |
| close(pipefd[0]); |
| } |
| |
| TEST_F(BinderLibTest, PromoteLocal) { |
| sp<IBinder> strong = new BBinder(); |
| wp<IBinder> weak = strong; |
| sp<IBinder> strong_from_weak = weak.promote(); |
| EXPECT_TRUE(strong != NULL); |
| EXPECT_EQ(strong, strong_from_weak); |
| strong = NULL; |
| strong_from_weak = NULL; |
| strong_from_weak = weak.promote(); |
| EXPECT_TRUE(strong_from_weak == NULL); |
| } |
| |
| TEST_F(BinderLibTest, PromoteRemote) { |
| int ret; |
| Parcel data, reply; |
| sp<IBinder> strong = new BBinder(); |
| sp<IBinder> server = addServer(); |
| |
| ASSERT_TRUE(server != NULL); |
| ASSERT_TRUE(strong != NULL); |
| |
| ret = data.writeWeakBinder(strong); |
| EXPECT_EQ(NO_ERROR, ret); |
| |
| ret = server->transact(BINDER_LIB_TEST_PROMOTE_WEAK_REF_TRANSACTION, data, &reply); |
| EXPECT_GE(ret, 0); |
| } |
| |
| TEST_F(BinderLibTest, CheckHandleZeroBinderHighBitsZeroCookie) { |
| status_t ret; |
| Parcel data, reply; |
| |
| ret = m_server->transact(BINDER_LIB_TEST_GET_SELF_TRANSACTION, data, &reply); |
| EXPECT_EQ(NO_ERROR, ret); |
| |
| const flat_binder_object *fb = reply.readObject(false); |
| ASSERT_TRUE(fb != NULL); |
| EXPECT_EQ(fb->type, BINDER_TYPE_HANDLE); |
| EXPECT_EQ(ProcessState::self()->getStrongProxyForHandle(fb->handle), m_server); |
| EXPECT_EQ(fb->cookie, (binder_uintptr_t)0); |
| EXPECT_EQ(fb->binder >> 32, (binder_uintptr_t)0); |
| } |
| |
| TEST_F(BinderLibTest, FreedBinder) { |
| status_t ret; |
| |
| sp<IBinder> server = addServer(); |
| ASSERT_TRUE(server != NULL); |
| |
| __u32 freedHandle; |
| wp<IBinder> keepFreedBinder; |
| { |
| Parcel data, reply; |
| data.writeBool(false); /* request weak reference */ |
| ret = server->transact(BINDER_LIB_TEST_CREATE_BINDER_TRANSACTION, data, &reply); |
| ASSERT_EQ(NO_ERROR, ret); |
| struct flat_binder_object *freed = (struct flat_binder_object *)(reply.data()); |
| freedHandle = freed->handle; |
| /* Add a weak ref to the freed binder so the driver does not |
| * delete its reference to it - otherwise the transaction |
| * fails regardless of whether the driver is fixed. |
| */ |
| keepFreedBinder = reply.readWeakBinder(); |
| } |
| { |
| Parcel data, reply; |
| data.writeStrongBinder(server); |
| /* Replace original handle with handle to the freed binder */ |
| struct flat_binder_object *strong = (struct flat_binder_object *)(data.data()); |
| __u32 oldHandle = strong->handle; |
| strong->handle = freedHandle; |
| ret = server->transact(BINDER_LIB_TEST_ADD_STRONG_REF_TRANSACTION, data, &reply); |
| /* Returns DEAD_OBJECT (-32) if target crashes and |
| * FAILED_TRANSACTION if the driver rejects the invalid |
| * object. |
| */ |
| EXPECT_EQ((status_t)FAILED_TRANSACTION, ret); |
| /* Restore original handle so parcel destructor does not use |
| * the wrong handle. |
| */ |
| strong->handle = oldHandle; |
| } |
| } |
| |
| class BinderLibTestService : public BBinder |
| { |
| public: |
| BinderLibTestService(int32_t id) |
| : m_id(id) |
| , m_nextServerId(id + 1) |
| , m_serverStartRequested(false) |
| { |
| pthread_mutex_init(&m_serverWaitMutex, NULL); |
| pthread_cond_init(&m_serverWaitCond, NULL); |
| } |
| ~BinderLibTestService() |
| { |
| exit(EXIT_SUCCESS); |
| } |
| virtual status_t onTransact(uint32_t code, |
| const Parcel& data, Parcel* reply, |
| uint32_t flags = 0) { |
| //printf("%s: code %d\n", __func__, code); |
| (void)flags; |
| |
| if (getuid() != (uid_t)IPCThreadState::self()->getCallingUid()) { |
| return PERMISSION_DENIED; |
| } |
| switch (code) { |
| case BINDER_LIB_TEST_REGISTER_SERVER: { |
| int32_t id; |
| sp<IBinder> binder; |
| id = data.readInt32(); |
| binder = data.readStrongBinder(); |
| if (binder == NULL) { |
| return BAD_VALUE; |
| } |
| |
| if (m_id != 0) |
| return INVALID_OPERATION; |
| |
| pthread_mutex_lock(&m_serverWaitMutex); |
| if (m_serverStartRequested) { |
| m_serverStartRequested = false; |
| m_serverStarted = binder; |
| pthread_cond_signal(&m_serverWaitCond); |
| } |
| pthread_mutex_unlock(&m_serverWaitMutex); |
| return NO_ERROR; |
| } |
| case BINDER_LIB_TEST_ADD_SERVER: { |
| int ret; |
| uint8_t buf[1] = { 0 }; |
| int serverid; |
| |
| if (m_id != 0) { |
| return INVALID_OPERATION; |
| } |
| pthread_mutex_lock(&m_serverWaitMutex); |
| if (m_serverStartRequested) { |
| ret = -EBUSY; |
| } else { |
| serverid = m_nextServerId++; |
| m_serverStartRequested = true; |
| |
| pthread_mutex_unlock(&m_serverWaitMutex); |
| ret = start_server_process(serverid); |
| pthread_mutex_lock(&m_serverWaitMutex); |
| } |
| if (ret > 0) { |
| if (m_serverStartRequested) { |
| struct timespec ts; |
| clock_gettime(CLOCK_REALTIME, &ts); |
| ts.tv_sec += 5; |
| ret = pthread_cond_timedwait(&m_serverWaitCond, &m_serverWaitMutex, &ts); |
| } |
| if (m_serverStartRequested) { |
| m_serverStartRequested = false; |
| ret = -ETIMEDOUT; |
| } else { |
| reply->writeStrongBinder(m_serverStarted); |
| reply->writeInt32(serverid); |
| m_serverStarted = NULL; |
| ret = NO_ERROR; |
| } |
| } else if (ret >= 0) { |
| m_serverStartRequested = false; |
| ret = UNKNOWN_ERROR; |
| } |
| pthread_mutex_unlock(&m_serverWaitMutex); |
| return ret; |
| } |
| case BINDER_LIB_TEST_NOP_TRANSACTION: |
| return NO_ERROR; |
| case BINDER_LIB_TEST_NOP_CALL_BACK: { |
| Parcel data2, reply2; |
| sp<IBinder> binder; |
| binder = data.readStrongBinder(); |
| if (binder == NULL) { |
| return BAD_VALUE; |
| } |
| reply2.writeInt32(NO_ERROR); |
| binder->transact(BINDER_LIB_TEST_CALL_BACK, data2, &reply2); |
| return NO_ERROR; |
| } |
| case BINDER_LIB_TEST_GET_SELF_TRANSACTION: |
| reply->writeStrongBinder(this); |
| return NO_ERROR; |
| case BINDER_LIB_TEST_GET_ID_TRANSACTION: |
| reply->writeInt32(m_id); |
| return NO_ERROR; |
| case BINDER_LIB_TEST_INDIRECT_TRANSACTION: { |
| int32_t count; |
| uint32_t indirect_code; |
| sp<IBinder> binder; |
| |
| count = data.readInt32(); |
| reply->writeInt32(m_id); |
| reply->writeInt32(count); |
| for (int i = 0; i < count; i++) { |
| binder = data.readStrongBinder(); |
| if (binder == NULL) { |
| return BAD_VALUE; |
| } |
| indirect_code = data.readInt32(); |
| BinderLibTestBundle data2(&data); |
| if (!data2.isValid()) { |
| return BAD_VALUE; |
| } |
| BinderLibTestBundle reply2; |
| binder->transact(indirect_code, data2, &reply2); |
| reply2.appendTo(reply); |
| } |
| return NO_ERROR; |
| } |
| case BINDER_LIB_TEST_SET_ERROR_TRANSACTION: |
| reply->setError(data.readInt32()); |
| return NO_ERROR; |
| case BINDER_LIB_TEST_GET_PTR_SIZE_TRANSACTION: |
| reply->writeInt32(sizeof(void *)); |
| return NO_ERROR; |
| case BINDER_LIB_TEST_GET_STATUS_TRANSACTION: |
| return NO_ERROR; |
| case BINDER_LIB_TEST_ADD_STRONG_REF_TRANSACTION: |
| m_strongRef = data.readStrongBinder(); |
| return NO_ERROR; |
| case BINDER_LIB_TEST_LINK_DEATH_TRANSACTION: { |
| int ret; |
| Parcel data2, reply2; |
| sp<TestDeathRecipient> testDeathRecipient = new TestDeathRecipient(); |
| sp<IBinder> target; |
| sp<IBinder> callback; |
| |
| target = data.readStrongBinder(); |
| if (target == NULL) { |
| return BAD_VALUE; |
| } |
| callback = data.readStrongBinder(); |
| if (callback == NULL) { |
| return BAD_VALUE; |
| } |
| ret = target->linkToDeath(testDeathRecipient); |
| if (ret == NO_ERROR) |
| ret = testDeathRecipient->waitEvent(5); |
| data2.writeInt32(ret); |
| callback->transact(BINDER_LIB_TEST_CALL_BACK, data2, &reply2); |
| return NO_ERROR; |
| } |
| case BINDER_LIB_TEST_WRITE_FILE_TRANSACTION: { |
| int ret; |
| int32_t size; |
| const void *buf; |
| int fd; |
| |
| fd = data.readFileDescriptor(); |
| if (fd < 0) { |
| return BAD_VALUE; |
| } |
| ret = data.readInt32(&size); |
| if (ret != NO_ERROR) { |
| return ret; |
| } |
| buf = data.readInplace(size); |
| if (buf == NULL) { |
| return BAD_VALUE; |
| } |
| ret = write(fd, buf, size); |
| if (ret != size) |
| return UNKNOWN_ERROR; |
| return NO_ERROR; |
| } |
| case BINDER_LIB_TEST_PROMOTE_WEAK_REF_TRANSACTION: { |
| int ret; |
| wp<IBinder> weak; |
| sp<IBinder> strong; |
| Parcel data2, reply2; |
| sp<IServiceManager> sm = defaultServiceManager(); |
| sp<IBinder> server = sm->getService(binderLibTestServiceName); |
| |
| weak = data.readWeakBinder(); |
| if (weak == NULL) { |
| return BAD_VALUE; |
| } |
| strong = weak.promote(); |
| |
| ret = server->transact(BINDER_LIB_TEST_NOP_TRANSACTION, data2, &reply2); |
| if (ret != NO_ERROR) |
| exit(EXIT_FAILURE); |
| |
| if (strong == NULL) { |
| reply->setError(1); |
| } |
| return NO_ERROR; |
| } |
| case BINDER_LIB_TEST_DELAYED_EXIT_TRANSACTION: |
| alarm(10); |
| return NO_ERROR; |
| case BINDER_LIB_TEST_EXIT_TRANSACTION: |
| while (wait(NULL) != -1 || errno != ECHILD) |
| ; |
| exit(EXIT_SUCCESS); |
| case BINDER_LIB_TEST_CREATE_BINDER_TRANSACTION: { |
| bool strongRef = data.readBool(); |
| sp<IBinder> binder = new BBinder(); |
| if (strongRef) { |
| reply->writeStrongBinder(binder); |
| } else { |
| reply->writeWeakBinder(binder); |
| } |
| return NO_ERROR; |
| } |
| default: |
| return UNKNOWN_TRANSACTION; |
| }; |
| } |
| private: |
| int32_t m_id; |
| int32_t m_nextServerId; |
| pthread_mutex_t m_serverWaitMutex; |
| pthread_cond_t m_serverWaitCond; |
| bool m_serverStartRequested; |
| sp<IBinder> m_serverStarted; |
| sp<IBinder> m_strongRef; |
| }; |
| |
| int run_server(int index, int readypipefd) |
| { |
| binderLibTestServiceName += String16(binderserversuffix); |
| |
| status_t ret; |
| sp<IServiceManager> sm = defaultServiceManager(); |
| { |
| sp<BinderLibTestService> testService = new BinderLibTestService(index); |
| if (index == 0) { |
| ret = sm->addService(binderLibTestServiceName, testService); |
| } else { |
| sp<IBinder> server = sm->getService(binderLibTestServiceName); |
| Parcel data, reply; |
| data.writeInt32(index); |
| data.writeStrongBinder(testService); |
| |
| ret = server->transact(BINDER_LIB_TEST_REGISTER_SERVER, data, &reply); |
| } |
| } |
| write(readypipefd, &ret, sizeof(ret)); |
| close(readypipefd); |
| //printf("%s: ret %d\n", __func__, ret); |
| if (ret) |
| return 1; |
| //printf("%s: joinThreadPool\n", __func__); |
| ProcessState::self()->startThreadPool(); |
| IPCThreadState::self()->joinThreadPool(); |
| //printf("%s: joinThreadPool returned\n", __func__); |
| return 1; /* joinThreadPool should not return */ |
| } |
| |
| int main(int argc, char **argv) { |
| int ret; |
| |
| if (argc == 4 && !strcmp(argv[1], "--servername")) { |
| binderservername = argv[2]; |
| } else { |
| binderservername = argv[0]; |
| } |
| |
| if (argc == 5 && !strcmp(argv[1], binderserverarg)) { |
| binderserversuffix = argv[4]; |
| return run_server(atoi(argv[2]), atoi(argv[3])); |
| } |
| binderserversuffix = new char[16]; |
| snprintf(binderserversuffix, 16, "%d", getpid()); |
| binderLibTestServiceName += String16(binderserversuffix); |
| |
| ::testing::InitGoogleTest(&argc, argv); |
| binder_env = AddGlobalTestEnvironment(new BinderLibTestEnv()); |
| ProcessState::self()->startThreadPool(); |
| return RUN_ALL_TESTS(); |
| } |
| |