Sanity check IMemory access versus underlying mmap am: 94b0d4e3ab am: ef6908e2b3 am: 97f49e50de
am: 84f488f354
* commit '84f488f354c33a7ece30cb569c85cd91f0259066':
Sanity check IMemory access versus underlying mmap
diff --git a/AppOpsManager.cpp b/AppOpsManager.cpp
index 61b4f7d..c562c30 100644
--- a/AppOpsManager.cpp
+++ b/AppOpsManager.cpp
@@ -44,7 +44,7 @@
int64_t startTime = 0;
mLock.lock();
sp<IAppOpsService> service = mService;
- while (service == NULL || !service->asBinder()->isBinderAlive()) {
+ while (service == NULL || !IInterface::asBinder(service)->isBinderAlive()) {
sp<IBinder> binder = defaultServiceManager()->checkService(_appops);
if (binder == NULL) {
// Wait for the app ops service to come back...
diff --git a/Binder.cpp b/Binder.cpp
index 2554351..9d200fb 100644
--- a/Binder.cpp
+++ b/Binder.cpp
@@ -72,7 +72,7 @@
BBinder::BBinder()
{
- atomic_init(&mExtras, 0);
+ atomic_init(&mExtras, static_cast<uintptr_t>(0));
}
bool BBinder::isBinderAlive() const
@@ -160,10 +160,18 @@
e->mObjects.attach(objectID, object, cleanupCookie, func);
}
+// The C11 standard doesn't allow atomic loads from const fields,
+// though C++11 does. Fudge it until standards get straightened out.
+static inline uintptr_t load_const_atomic(const atomic_uintptr_t* p,
+ memory_order mo) {
+ atomic_uintptr_t* non_const_p = const_cast<atomic_uintptr_t*>(p);
+ return atomic_load_explicit(non_const_p, mo);
+}
+
void* BBinder::findObject(const void* objectID) const
{
Extras* e = reinterpret_cast<Extras*>(
- atomic_load_explicit(&mExtras, memory_order_acquire));
+ load_const_atomic(&mExtras, memory_order_acquire));
if (!e) return NULL;
AutoMutex _l(e->mLock);
diff --git a/BpBinder.cpp b/BpBinder.cpp
index 101de7e..345ba20 100644
--- a/BpBinder.cpp
+++ b/BpBinder.cpp
@@ -220,7 +220,6 @@
if ((obit.recipient == recipient
|| (recipient == NULL && obit.cookie == cookie))
&& obit.flags == flags) {
- const uint32_t allFlags = obit.flags|flags;
if (outRecipient != NULL) {
*outRecipient = mObituaries->itemAt(i).recipient;
}
diff --git a/Debug.cpp b/Debug.cpp
index 0ffafbb..bdb7182 100644
--- a/Debug.cpp
+++ b/Debug.cpp
@@ -220,13 +220,8 @@
for (word = 0; word < bytesPerLine; ) {
-#ifdef HAVE_LITTLE_ENDIAN
const size_t startIndex = word+(alignment-(alignment?1:0));
const ssize_t dir = -1;
-#else
- const size_t startIndex = word;
- const ssize_t dir = 1;
-#endif
for (index = 0; index < alignment || (alignment == 0 && index < bytesPerLine); index++) {
diff --git a/IAppOpsService.cpp b/IAppOpsService.cpp
index f58a352..86abdc0 100644
--- a/IAppOpsService.cpp
+++ b/IAppOpsService.cpp
@@ -91,14 +91,14 @@
data.writeInterfaceToken(IAppOpsService::getInterfaceDescriptor());
data.writeInt32(op);
data.writeString16(packageName);
- data.writeStrongBinder(callback->asBinder());
+ data.writeStrongBinder(IInterface::asBinder(callback));
remote()->transact(START_WATCHING_MODE_TRANSACTION, data, &reply);
}
virtual void stopWatchingMode(const sp<IAppOpsCallback>& callback) {
Parcel data, reply;
data.writeInterfaceToken(IAppOpsService::getInterfaceDescriptor());
- data.writeStrongBinder(callback->asBinder());
+ data.writeStrongBinder(IInterface::asBinder(callback));
remote()->transact(STOP_WATCHING_MODE_TRANSACTION, data, &reply);
}
diff --git a/IInterface.cpp b/IInterface.cpp
index 29acf5d..8c60dc4 100644
--- a/IInterface.cpp
+++ b/IInterface.cpp
@@ -27,14 +27,18 @@
IInterface::~IInterface() {
}
-sp<IBinder> IInterface::asBinder()
+// static
+sp<IBinder> IInterface::asBinder(const IInterface* iface)
{
- return this ? onAsBinder() : NULL;
+ if (iface == NULL) return NULL;
+ return const_cast<IInterface*>(iface)->onAsBinder();
}
-sp<const IBinder> IInterface::asBinder() const
+// static
+sp<IBinder> IInterface::asBinder(const sp<IInterface>& iface)
{
- return this ? const_cast<IInterface*>(this)->onAsBinder() : NULL;
+ if (iface == NULL) return NULL;
+ return iface->onAsBinder();
}
// ---------------------------------------------------------------------------
diff --git a/IMemory.cpp b/IMemory.cpp
index b9a8bce..fb8d620 100644
--- a/IMemory.cpp
+++ b/IMemory.cpp
@@ -228,7 +228,7 @@
CHECK_INTERFACE(IMemory, data, reply);
ssize_t offset;
size_t size;
- reply->writeStrongBinder( getMemory(&offset, &size)->asBinder() );
+ reply->writeStrongBinder( IInterface::asBinder(getMemory(&offset, &size)) );
reply->writeInt32(offset);
reply->writeInt32(size);
return NO_ERROR;
@@ -253,7 +253,7 @@
if (mRealHeap) {
// by construction we're the last one
if (mBase != MAP_FAILED) {
- sp<IBinder> binder = const_cast<BpMemoryHeap*>(this)->asBinder();
+ sp<IBinder> binder = IInterface::asBinder(this);
if (VERBOSE) {
ALOGD("UNMAPPING binder=%p, heap=%p, size=%zu, fd=%d",
@@ -265,7 +265,7 @@
}
} else {
// remove from list only if it was mapped before
- sp<IBinder> binder = const_cast<BpMemoryHeap*>(this)->asBinder();
+ sp<IBinder> binder = IInterface::asBinder(this);
free_heap(binder);
}
}
@@ -274,7 +274,7 @@
void BpMemoryHeap::assertMapped() const
{
if (mHeapId == -1) {
- sp<IBinder> binder(const_cast<BpMemoryHeap*>(this)->asBinder());
+ sp<IBinder> binder(IInterface::asBinder(const_cast<BpMemoryHeap*>(this)));
sp<BpMemoryHeap> heap(static_cast<BpMemoryHeap*>(find_heap(binder).get()));
heap->assertReallyMapped();
if (heap->mBase != MAP_FAILED) {
@@ -309,7 +309,8 @@
uint32_t offset = reply.readInt32();
ALOGE_IF(err, "binder=%p transaction failed fd=%d, size=%zd, err=%d (%s)",
- asBinder().get(), parcel_fd, size, err, strerror(-err));
+ IInterface::asBinder(this).get(),
+ parcel_fd, size, err, strerror(-err));
int fd = dup( parcel_fd );
ALOGE_IF(fd==-1, "cannot dup fd=%d, size=%zd, err=%d (%s)",
@@ -326,7 +327,7 @@
mBase = mmap(0, size, access, MAP_SHARED, fd, offset);
if (mBase == MAP_FAILED) {
ALOGE("cannot map BpMemoryHeap (binder=%p), size=%zd, fd=%d (%s)",
- asBinder().get(), size, fd, strerror(errno));
+ IInterface::asBinder(this).get(), size, fd, strerror(errno));
close(fd);
} else {
mSize = size;
diff --git a/IPCThreadState.cpp b/IPCThreadState.cpp
index dd04dcf..9f68aa8 100644
--- a/IPCThreadState.cpp
+++ b/IPCThreadState.cpp
@@ -29,21 +29,14 @@
#include <private/binder/binder_module.h>
#include <private/binder/Static.h>
-#include <sys/ioctl.h>
-#include <signal.h>
#include <errno.h>
-#include <stdio.h>
-#include <unistd.h>
-
-#ifdef HAVE_PTHREADS
#include <pthread.h>
#include <sched.h>
+#include <signal.h>
+#include <stdio.h>
+#include <sys/ioctl.h>
#include <sys/resource.h>
-#endif
-#ifdef HAVE_WIN32_THREADS
-#include <windows.h>
-#endif
-
+#include <unistd.h>
#if LOG_NDEBUG
@@ -70,13 +63,11 @@
namespace android {
static const char* getReturnString(size_t idx);
-static const char* getCommandString(size_t idx);
static const void* printReturnCommand(TextOutput& out, const void* _cmd);
static const void* printCommand(TextOutput& out, const void* _cmd);
-// This will result in a missing symbol failure if the IF_LOG_COMMANDS()
-// conditionals don't get stripped... but that is probably what we want.
-#if !LOG_NDEBUG
+// Static const and functions will be optimized out if not used,
+// when LOG_NDEBUG and references in IF_LOG_COMMANDS() are optimized out.
static const char *kReturnStrings[] = {
"BR_ERROR",
"BR_OK",
@@ -126,14 +117,6 @@
return "unknown";
}
-static const char* getCommandString(size_t idx)
-{
- if (idx < sizeof(kCommandStrings) / sizeof(kCommandStrings[0]))
- return kCommandStrings[idx];
- else
- return "unknown";
-}
-
static const void* printBinderTransactionData(TextOutput& out, const void* data)
{
const binder_transaction_data* btd =
@@ -145,7 +128,7 @@
out << "target.ptr=" << btd->target.ptr;
}
out << " (cookie " << btd->cookie << ")" << endl
- << "code=" << TypeCode(btd->code) << ", flags=" << (void*)btd->flags << endl
+ << "code=" << TypeCode(btd->code) << ", flags=" << (void*)(long)btd->flags << endl
<< "data=" << btd->data.ptr.buffer << " (" << (void*)btd->data_size
<< " bytes)" << endl
<< "offsets=" << btd->data.ptr.offsets << " (" << (void*)btd->offsets_size
@@ -157,10 +140,10 @@
{
static const size_t N = sizeof(kReturnStrings)/sizeof(kReturnStrings[0]);
const int32_t* cmd = (const int32_t*)_cmd;
- int32_t code = *cmd++;
+ uint32_t code = (uint32_t)*cmd++;
size_t cmdIndex = code & 0xff;
- if (code == (int32_t) BR_ERROR) {
- out << "BR_ERROR: " << (void*)(*cmd++) << endl;
+ if (code == BR_ERROR) {
+ out << "BR_ERROR: " << (void*)(long)(*cmd++) << endl;
return cmd;
} else if (cmdIndex >= N) {
out << "Unknown reply: " << code << endl;
@@ -187,21 +170,21 @@
case BR_DECREFS: {
const int32_t b = *cmd++;
const int32_t c = *cmd++;
- out << ": target=" << (void*)b << " (cookie " << (void*)c << ")";
+ out << ": target=" << (void*)(long)b << " (cookie " << (void*)(long)c << ")";
} break;
case BR_ATTEMPT_ACQUIRE: {
const int32_t p = *cmd++;
const int32_t b = *cmd++;
const int32_t c = *cmd++;
- out << ": target=" << (void*)b << " (cookie " << (void*)c
+ out << ": target=" << (void*)(long)b << " (cookie " << (void*)(long)c
<< "), pri=" << p;
} break;
case BR_DEAD_BINDER:
case BR_CLEAR_DEATH_NOTIFICATION_DONE: {
const int32_t c = *cmd++;
- out << ": death cookie " << (void*)c;
+ out << ": death cookie " << (void*)(long)c;
} break;
default:
@@ -218,7 +201,7 @@
{
static const size_t N = sizeof(kCommandStrings)/sizeof(kCommandStrings[0]);
const int32_t* cmd = (const int32_t*)_cmd;
- int32_t code = *cmd++;
+ uint32_t code = (uint32_t)*cmd++;
size_t cmdIndex = code & 0xff;
if (cmdIndex >= N) {
@@ -242,7 +225,7 @@
case BC_FREE_BUFFER: {
const int32_t buf = *cmd++;
- out << ": buffer=" << (void*)buf;
+ out << ": buffer=" << (void*)(long)buf;
} break;
case BC_INCREFS:
@@ -257,7 +240,7 @@
case BC_ACQUIRE_DONE: {
const int32_t b = *cmd++;
const int32_t c = *cmd++;
- out << ": target=" << (void*)b << " (cookie " << (void*)c << ")";
+ out << ": target=" << (void*)(long)b << " (cookie " << (void*)(long)c << ")";
} break;
case BC_ATTEMPT_ACQUIRE: {
@@ -270,12 +253,12 @@
case BC_CLEAR_DEATH_NOTIFICATION: {
const int32_t h = *cmd++;
const int32_t c = *cmd++;
- out << ": handle=" << h << " (death cookie " << (void*)c << ")";
+ out << ": handle=" << h << " (death cookie " << (void*)(long)c << ")";
} break;
case BC_DEAD_BINDER_DONE: {
const int32_t c = *cmd++;
- out << ": death cookie " << (void*)c;
+ out << ": death cookie " << (void*)(long)c;
} break;
default:
@@ -287,7 +270,6 @@
out << endl;
return cmd;
}
-#endif
static pthread_mutex_t gTLSMutex = PTHREAD_MUTEX_INITIALIZER;
static bool gHaveTLS = false;
@@ -361,12 +343,12 @@
return err;
}
-int IPCThreadState::getCallingPid() const
+pid_t IPCThreadState::getCallingPid() const
{
return mCallingPid;
}
-int IPCThreadState::getCallingUid() const
+uid_t IPCThreadState::getCallingUid() const
{
return mCallingUid;
}
@@ -682,7 +664,7 @@
IPCThreadState::IPCThreadState()
: mProcess(ProcessState::self()),
- mMyThreadId(androidGetTid()),
+ mMyThreadId(gettid()),
mStrictModePolicy(0),
mLastTransactionBinderFlags(0)
{
@@ -708,7 +690,7 @@
status_t IPCThreadState::waitForResponse(Parcel *reply, status_t *acquireResult)
{
- int32_t cmd;
+ uint32_t cmd;
int32_t err;
while (1) {
@@ -717,7 +699,7 @@
if (err < NO_ERROR) break;
if (mIn.dataAvail() == 0) continue;
- cmd = mIn.readInt32();
+ cmd = (uint32_t)mIn.readInt32();
IF_LOG_COMMANDS() {
alog << "Processing waitForResponse Command: "
@@ -947,7 +929,7 @@
RefBase::weakref_type* refs;
status_t result = NO_ERROR;
- switch (cmd) {
+ switch ((uint32_t)cmd) {
case BR_ERROR:
result = mIn.readInt32();
break;
diff --git a/IServiceManager.cpp b/IServiceManager.cpp
index 7b1b0e7..3c716df 100644
--- a/IServiceManager.cpp
+++ b/IServiceManager.cpp
@@ -87,7 +87,7 @@
}
// Is this a permission failure, or did the controller go away?
- if (pc->asBinder()->isBinderAlive()) {
+ if (IInterface::asBinder(pc)->isBinderAlive()) {
ALOGW("Permission failure: %s from uid=%d pid=%d",
String8(permission).string(), uid, pid);
return false;
diff --git a/Parcel.cpp b/Parcel.cpp
index 87ce5d0..d769caa 100644
--- a/Parcel.cpp
+++ b/Parcel.cpp
@@ -57,7 +57,7 @@
#define PAD_SIZE(s) (((s)+3)&~3)
// Note: must be kept in sync with android/os/StrictMode.java's PENALTY_GATHER
-#define STRICT_MODE_PENALTY_GATHER 0x100
+#define STRICT_MODE_PENALTY_GATHER (0x40 << 16)
// Note: must be kept in sync with android/os/Parcel.java's EX_HAS_REPLY_HEADER
#define EX_HAS_REPLY_HEADER -128
@@ -645,6 +645,12 @@
{
return writeAligned(val);
}
+
+status_t Parcel::writeUint32(uint32_t val)
+{
+ return writeAligned(val);
+}
+
status_t Parcel::writeInt32Array(size_t len, const int32_t *val) {
if (!val) {
return writeAligned(-1);
@@ -1033,6 +1039,15 @@
return readAligned<int32_t>();
}
+status_t Parcel::readUint32(uint32_t *pArg) const
+{
+ return readAligned(pArg);
+}
+
+uint32_t Parcel::readUint32() const
+{
+ return readAligned<uint32_t>();
+}
status_t Parcel::readInt64(int64_t *pArg) const
{
@@ -1442,7 +1457,7 @@
for (size_t i = 0; i < mObjectsSize; i++) {
binder_size_t offset = mObjects[i];
if (offset < minOffset) {
- ALOGE("%s: bad object offset %"PRIu64" < %"PRIu64"\n",
+ ALOGE("%s: bad object offset %" PRIu64 " < %" PRIu64 "\n",
__func__, (uint64_t)offset, (uint64_t)minOffset);
mObjectsSize = 0;
break;
diff --git a/include/hwbinder/IInterface.h b/include/hwbinder/IInterface.h
index 5f9f69c..4ce3613 100644
--- a/include/hwbinder/IInterface.h
+++ b/include/hwbinder/IInterface.h
@@ -28,9 +28,9 @@
{
public:
IInterface();
- sp<IBinder> asBinder();
- sp<const IBinder> asBinder() const;
-
+ static sp<IBinder> asBinder(const IInterface*);
+ static sp<IBinder> asBinder(const sp<IInterface>&);
+
protected:
virtual ~IInterface();
virtual IBinder* onAsBinder() = 0;
diff --git a/include/hwbinder/IPCThreadState.h b/include/hwbinder/IPCThreadState.h
index 6e0c01b..60c2242 100644
--- a/include/hwbinder/IPCThreadState.h
+++ b/include/hwbinder/IPCThreadState.h
@@ -22,7 +22,7 @@
#include <binder/ProcessState.h>
#include <utils/Vector.h>
-#ifdef HAVE_WIN32_PROC
+#if defined(_WIN32)
typedef int uid_t;
#endif
@@ -39,8 +39,8 @@
status_t clearLastError();
- int getCallingPid() const;
- int getCallingUid() const;
+ pid_t getCallingPid() const;
+ uid_t getCallingUid() const;
void setStrictModePolicy(int32_t policy);
int32_t getStrictModePolicy() const;
diff --git a/include/hwbinder/Parcel.h b/include/hwbinder/Parcel.h
index 6a69761..a52e044 100644
--- a/include/hwbinder/Parcel.h
+++ b/include/hwbinder/Parcel.h
@@ -94,6 +94,7 @@
void* writeInplace(size_t len);
status_t writeUnpadded(const void* data, size_t len);
status_t writeInt32(int32_t val);
+ status_t writeUint32(uint32_t val);
status_t writeInt64(int64_t val);
status_t writeFloat(float val);
status_t writeDouble(double val);
@@ -152,6 +153,8 @@
const void* readInplace(size_t len) const;
int32_t readInt32() const;
status_t readInt32(int32_t *pArg) const;
+ uint32_t readUint32() const;
+ status_t readUint32(uint32_t *pArg) const;
int64_t readInt64() const;
status_t readInt64(int64_t *pArg) const;
float readFloat() const;
diff --git a/tests/Android.mk b/tests/Android.mk
new file mode 100644
index 0000000..3668729
--- /dev/null
+++ b/tests/Android.mk
@@ -0,0 +1,34 @@
+#
+# 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.
+#
+
+LOCAL_PATH:= $(call my-dir)
+
+include $(CLEAR_VARS)
+ifneq ($(TARGET_USES_64_BIT_BINDER),true)
+ifneq ($(TARGET_IS_64_BIT),true)
+LOCAL_CFLAGS += -DBINDER_IPC_32BIT=1
+endif
+endif
+
+LOCAL_MODULE := binderDriverInterfaceTest
+LOCAL_SRC_FILES := binderDriverInterfaceTest.cpp
+include $(BUILD_NATIVE_TEST)
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := binderLibTest
+LOCAL_SRC_FILES := binderLibTest.cpp
+LOCAL_SHARED_LIBRARIES := libbinder libutils
+include $(BUILD_NATIVE_TEST)
diff --git a/tests/binderDriverInterfaceTest.cpp b/tests/binderDriverInterfaceTest.cpp
new file mode 100644
index 0000000..315f349
--- /dev/null
+++ b/tests/binderDriverInterfaceTest.cpp
@@ -0,0 +1,353 @@
+/*
+ * 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 <stdio.h>
+#include <stdlib.h>
+
+#include <gtest/gtest.h>
+#include <linux/binder.h>
+#include <binder/IBinder.h>
+#include <sys/mman.h>
+#include <poll.h>
+
+#define BINDER_DEV_NAME "/dev/binder"
+
+testing::Environment* binder_env;
+
+class BinderDriverInterfaceTestEnv : public ::testing::Environment {
+ virtual void SetUp() {
+ int ret;
+ uint32_t max_threads = 0;
+
+ m_binderFd = open(BINDER_DEV_NAME, O_RDWR | O_NONBLOCK);
+ ASSERT_GE(m_binderFd, 0);
+ m_buffer = mmap(NULL, 64*1024, PROT_READ, MAP_SHARED, m_binderFd, 0);
+ ASSERT_NE(m_buffer, (void *)NULL);
+ ret = ioctl(m_binderFd, BINDER_SET_MAX_THREADS, &max_threads);
+ EXPECT_EQ(0, ret);
+ EnterLooper();
+ }
+ virtual void TearDown() {
+ close(m_binderFd);
+ }
+ private:
+ int m_binderFd;
+ void *m_buffer;
+ public:
+ int getBinderFd(void) {
+ return m_binderFd;
+ }
+ void EnterLooper(void) {
+ int ret;
+ const uint32_t bc[] = {
+ BC_ENTER_LOOPER,
+ };
+ struct binder_write_read bwr = binder_write_read();
+ bwr.write_buffer = (uintptr_t)bc;
+ bwr.write_size = sizeof(bc);
+ ret = ioctl(m_binderFd, BINDER_WRITE_READ, &bwr);
+ EXPECT_EQ(0, ret);
+ if (ret < 0) {
+ EXPECT_EQ(0, errno);
+ }
+ EXPECT_EQ(sizeof(bc), bwr.write_consumed);
+ }
+};
+
+class BinderDriverInterfaceTest : public ::testing::Test {
+ public:
+ virtual void SetUp() {
+ m_binderFd = static_cast<BinderDriverInterfaceTestEnv *>(binder_env)->getBinderFd();
+ }
+ virtual void TearDown() {
+ }
+ protected:
+ void binderTestIoctlRetErr2(int cmd, void *arg, int expect_ret, int expect_errno, int accept_errno) {
+ int ret;
+
+ ret = ioctl(m_binderFd, cmd, arg);
+ EXPECT_EQ(expect_ret, ret);
+ if (ret < 0) {
+ if (errno != accept_errno)
+ EXPECT_EQ(expect_errno, errno);
+ }
+ }
+ void binderTestIoctlErr2(int cmd, void *arg, int expect_errno, int accept_errno) {
+ binderTestIoctlRetErr2(cmd, arg, -1, expect_errno, accept_errno);
+ }
+ void binderTestIoctlErr1(int cmd, void *arg, int expect_errno) {
+ binderTestIoctlErr2(cmd, arg, expect_errno, expect_errno);
+ }
+ void binderTestIoctl(int cmd, void *arg) {
+ binderTestIoctlRetErr2(cmd, arg, 0, 0, 0);
+ }
+ void binderTestIoctlUnimplemented(int cmd, void *arg) {
+ int ret;
+
+ ret = ioctl(m_binderFd, cmd, arg);
+ if (ret < 0) {
+ /* Not currently implmented. Allow ret == -1, errno == EINVAL */
+ EXPECT_EQ(-1, ret);
+ EXPECT_EQ(EINVAL, errno);
+ }
+ }
+ void binderTestReadEmpty(void) {
+ size_t i;
+ uint32_t br[32];
+ struct binder_write_read bwr = binder_write_read();
+ SCOPED_TRACE("TestReadEmpty");
+ bwr.read_buffer = (uintptr_t)br;
+ bwr.read_size = sizeof(br);
+ binderTestIoctlErr1(BINDER_WRITE_READ, &bwr, EAGAIN);
+ EXPECT_EQ(0u, bwr.read_consumed);
+ for (i = 0; i * sizeof(uint32_t) < bwr.read_consumed; i++) {
+ SCOPED_TRACE(testing::Message() << "i = " << i);
+ EXPECT_EQ(BR_NOOP, br[i]);
+ }
+ }
+ void binderWaitForReadData(int timeout_ms) {
+ int ret;
+ pollfd pfd = pollfd();
+
+ pfd.fd = m_binderFd;
+ pfd.events = POLLIN;
+ ret = poll(&pfd, 1, timeout_ms);
+ EXPECT_EQ(1, ret);
+ }
+ private:
+ int m_binderFd;
+};
+
+TEST_F(BinderDriverInterfaceTest, Version) {
+ struct binder_version version;
+ binderTestIoctl(BINDER_VERSION, &version);
+ ASSERT_EQ(BINDER_CURRENT_PROTOCOL_VERSION, version.protocol_version);
+}
+
+TEST_F(BinderDriverInterfaceTest, WriteReadNull) {
+ binderTestIoctlErr1(BINDER_WRITE_READ, NULL, EFAULT);
+}
+
+TEST_F(BinderDriverInterfaceTest, SetIdleTimeoutNull) {
+ binderTestIoctlErr2(BINDER_SET_IDLE_TIMEOUT, NULL, EFAULT, EINVAL);
+}
+
+TEST_F(BinderDriverInterfaceTest, SetMaxThreadsNull) {
+ binderTestIoctlErr2(BINDER_SET_MAX_THREADS, NULL, EFAULT, EINVAL); /* TODO: don't accept EINVAL */
+}
+
+TEST_F(BinderDriverInterfaceTest, SetIdlePriorityNull) {
+ binderTestIoctlErr2(BINDER_SET_IDLE_PRIORITY, NULL, EFAULT, EINVAL);
+}
+
+TEST_F(BinderDriverInterfaceTest, VersionNull) {
+ binderTestIoctlErr2(BINDER_VERSION, NULL, EFAULT, EINVAL); /* TODO: don't accept EINVAL */
+}
+
+TEST_F(BinderDriverInterfaceTest, SetIdleTimeoutNoTest) {
+ int64_t idle_timeout = 100000;
+ binderTestIoctlUnimplemented(BINDER_SET_IDLE_TIMEOUT, &idle_timeout);
+}
+
+TEST_F(BinderDriverInterfaceTest, SetMaxThreads) {
+ uint32_t max_threads = 0;
+ binderTestIoctl(BINDER_SET_MAX_THREADS, &max_threads);
+}
+
+TEST_F(BinderDriverInterfaceTest, SetIdlePriorityNoTest) {
+ int idle_priority = 0;
+ binderTestIoctlUnimplemented(BINDER_SET_IDLE_PRIORITY, &idle_priority);
+}
+
+TEST_F(BinderDriverInterfaceTest, SetContextMgrBusy) {
+ int32_t dummy = 0;
+ binderTestIoctlErr1(BINDER_SET_CONTEXT_MGR, &dummy, EBUSY);
+}
+
+TEST_F(BinderDriverInterfaceTest, ThreadExit) {
+ int32_t dummy = 0;
+ binderTestIoctl(BINDER_THREAD_EXIT, &dummy);
+ static_cast<BinderDriverInterfaceTestEnv *>(binder_env)->EnterLooper();
+}
+
+TEST_F(BinderDriverInterfaceTest, WriteReadEmpty) {
+ struct binder_write_read bwr = binder_write_read();
+ binderTestIoctl(BINDER_WRITE_READ, &bwr);
+}
+
+TEST_F(BinderDriverInterfaceTest, Read) {
+ binderTestReadEmpty();
+}
+
+TEST_F(BinderDriverInterfaceTest, IncRefsAcquireReleaseDecRefs) {
+ const uint32_t bc[] = {
+ BC_INCREFS,
+ 0,
+ BC_ACQUIRE,
+ 0,
+ BC_RELEASE,
+ 0,
+ BC_DECREFS,
+ 0,
+ };
+ struct binder_write_read bwr = binder_write_read();
+ bwr.write_buffer = (uintptr_t)bc;
+ bwr.write_size = sizeof(bc);
+ binderTestIoctl(BINDER_WRITE_READ, &bwr);
+ EXPECT_EQ(sizeof(bc), bwr.write_consumed);
+ binderTestReadEmpty();
+}
+
+TEST_F(BinderDriverInterfaceTest, Transaction) {
+ binder_uintptr_t cookie = 1234;
+ struct {
+ uint32_t cmd1;
+ struct binder_transaction_data arg1;
+ } __attribute__((packed)) bc1 = {
+ .cmd1 = BC_TRANSACTION,
+ .arg1 = {
+ .target = { 0 },
+ .cookie = 0,
+ .code = android::IBinder::PING_TRANSACTION,
+ .flags = 0,
+ .sender_pid = 0,
+ .sender_euid = 0,
+ .data_size = 0,
+ .offsets_size = 0,
+ .data = {0, 0},
+ },
+ };
+ struct {
+ uint32_t cmd0;
+ uint32_t cmd1;
+ uint32_t cmd2;
+ binder_transaction_data arg2;
+ uint32_t pad[16];
+ } __attribute__((packed)) br;
+ struct binder_write_read bwr = binder_write_read();
+
+ bwr.write_buffer = (uintptr_t)&bc1;
+ bwr.write_size = sizeof(bc1);
+ bwr.read_buffer = (uintptr_t)&br;
+ bwr.read_size = sizeof(br);
+
+ {
+ SCOPED_TRACE("1st WriteRead");
+ binderTestIoctl(BINDER_WRITE_READ, &bwr);
+ }
+ EXPECT_EQ(sizeof(bc1), bwr.write_consumed);
+ if (bwr.read_consumed < offsetof(typeof(br), pad)) {
+ SCOPED_TRACE("2nd WriteRead");
+ binderWaitForReadData(10000);
+ binderTestIoctl(BINDER_WRITE_READ, &bwr);
+ }
+ EXPECT_EQ(offsetof(typeof(br), pad), bwr.read_consumed);
+ if (bwr.read_consumed > offsetof(typeof(br), cmd0))
+ EXPECT_EQ(BR_NOOP, br.cmd0);
+ if (bwr.read_consumed > offsetof(typeof(br), cmd1))
+ EXPECT_EQ(BR_TRANSACTION_COMPLETE, br.cmd1);
+ if (bwr.read_consumed > offsetof(typeof(br), cmd2))
+ EXPECT_EQ(BR_REPLY, br.cmd2);
+ if (bwr.read_consumed >= offsetof(typeof(br), pad)) {
+ EXPECT_EQ(0u, br.arg2.target.ptr);
+ EXPECT_EQ(0u, br.arg2.cookie);
+ EXPECT_EQ(0u, br.arg2.code);
+ EXPECT_EQ(0u, br.arg2.flags);
+ EXPECT_EQ(0u, br.arg2.data_size);
+ EXPECT_EQ(0u, br.arg2.offsets_size);
+
+ SCOPED_TRACE("3rd WriteRead");
+
+ binderTestReadEmpty();
+
+ struct {
+ uint32_t cmd1;
+ binder_uintptr_t arg1;
+ } __attribute__((packed)) bc2 = {
+ .cmd1 = BC_FREE_BUFFER,
+ .arg1 = br.arg2.data.ptr.buffer,
+ };
+
+ bwr.write_buffer = (uintptr_t)&bc2;
+ bwr.write_size = sizeof(bc2);
+ bwr.write_consumed = 0;
+ bwr.read_size = 0;
+
+ binderTestIoctl(BINDER_WRITE_READ, &bwr);
+ EXPECT_EQ(sizeof(bc2), bwr.write_consumed);
+ }
+ binderTestReadEmpty();
+}
+
+TEST_F(BinderDriverInterfaceTest, RequestDeathNotification) {
+ binder_uintptr_t cookie = 1234;
+ struct {
+ uint32_t cmd0;
+ uint32_t arg0;
+ uint32_t cmd1;
+ struct binder_handle_cookie arg1;
+ uint32_t cmd2;
+ struct binder_handle_cookie arg2;
+ uint32_t cmd3;
+ uint32_t arg3;
+ } __attribute__((packed)) bc = {
+ .cmd0 = BC_INCREFS,
+ .arg0 = 0,
+ .cmd1 = BC_REQUEST_DEATH_NOTIFICATION,
+ .arg1 = {
+ .handle = 0,
+ .cookie = cookie,
+ },
+ .cmd2 = BC_CLEAR_DEATH_NOTIFICATION,
+ .arg2 = {
+ .handle = 0,
+ .cookie = cookie,
+ },
+ .cmd3 = BC_DECREFS,
+ .arg3 = 0,
+ };
+ struct {
+ uint32_t cmd0;
+ uint32_t cmd1;
+ binder_uintptr_t arg1;
+ uint32_t pad[16];
+ } __attribute__((packed)) br;
+ struct binder_write_read bwr = binder_write_read();
+
+ bwr.write_buffer = (uintptr_t)&bc;
+ bwr.write_size = sizeof(bc);
+ bwr.read_buffer = (uintptr_t)&br;
+ bwr.read_size = sizeof(br);
+
+ binderTestIoctl(BINDER_WRITE_READ, &bwr);
+ EXPECT_EQ(sizeof(bc), bwr.write_consumed);
+ EXPECT_EQ(sizeof(br) - sizeof(br.pad), bwr.read_consumed);
+ EXPECT_EQ(BR_NOOP, br.cmd0);
+ EXPECT_EQ(BR_CLEAR_DEATH_NOTIFICATION_DONE, br.cmd1);
+ EXPECT_EQ(cookie, br.arg1);
+ binderTestReadEmpty();
+}
+
+int main(int argc, char **argv) {
+ ::testing::InitGoogleTest(&argc, argv);
+
+ binder_env = AddGlobalTestEnvironment(new BinderDriverInterfaceTestEnv());
+
+ return RUN_ALL_TESTS();
+}
+
diff --git a/tests/binderLibTest.cpp b/tests/binderLibTest.cpp
new file mode 100644
index 0000000..3df3acf
--- /dev/null
+++ b/tests/binderLibTest.cpp
@@ -0,0 +1,954 @@
+/*
+ * 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 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_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,
+};
+
+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,
+ 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) {
+#if defined(HAVE_PTHREAD_COND_TIMEDWAIT_RELATIVE)
+ pthread_cond_timeout_np(&m_waitCond, &m_waitMutex, timeout_s * 1000);
+#else
+ struct timespec ts;
+ clock_gettime(CLOCK_REALTIME, &ts);
+ ts.tv_sec += timeout_s;
+ pthread_cond_timedwait(&m_waitCond, &m_waitMutex, &ts);
+#endif
+ }
+ 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), 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), 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), 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);
+}
+
+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) {
+#if defined(HAVE_PTHREAD_COND_TIMEDWAIT_RELATIVE)
+ ret = pthread_cond_timeout_np(&m_serverWaitCond, &m_serverWaitMutex, 5000);
+#else
+ struct timespec ts;
+ clock_gettime(CLOCK_REALTIME, &ts);
+ ts.tv_sec += 5;
+ ret = pthread_cond_timedwait(&m_serverWaitCond, &m_serverWaitMutex, &ts);
+#endif
+ }
+ 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_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);
+ 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)
+{
+ 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 == 3 && !strcmp(argv[1], "--servername")) {
+ binderservername = argv[2];
+ } else {
+ binderservername = argv[0];
+ }
+
+ if (argc == 4 && !strcmp(argv[1], binderserverarg)) {
+ return run_server(atoi(argv[2]), atoi(argv[3]));
+ }
+
+ ::testing::InitGoogleTest(&argc, argv);
+ binder_env = AddGlobalTestEnvironment(new BinderLibTestEnv());
+ ProcessState::self()->startThreadPool();
+ return RUN_ALL_TESTS();
+}
+