summaryrefslogtreecommitdiffstats
path: root/libs/binder/ProcessState.cpp
diff options
context:
space:
mode:
authorMathias Agopian <mathias@google.com>2009-05-18 15:08:03 -0700
committerMathias Agopian <mathias@google.com>2009-05-20 12:55:02 -0700
commit208059f67ed2dd9fa025e07fcb6954d3cb61c79e (patch)
treeb2fde632a7c9abf4137751ffe779318c0a89bd96 /libs/binder/ProcessState.cpp
parent501c6179e158b2d5bfc67cb8b7180c02ac301f97 (diff)
downloadframeworks_native-208059f67ed2dd9fa025e07fcb6954d3cb61c79e.zip
frameworks_native-208059f67ed2dd9fa025e07fcb6954d3cb61c79e.tar.gz
frameworks_native-208059f67ed2dd9fa025e07fcb6954d3cb61c79e.tar.bz2
checkpoint: split libutils into libutils + libbinder
Diffstat (limited to 'libs/binder/ProcessState.cpp')
-rw-r--r--libs/binder/ProcessState.cpp398
1 files changed, 398 insertions, 0 deletions
diff --git a/libs/binder/ProcessState.cpp b/libs/binder/ProcessState.cpp
new file mode 100644
index 0000000..3a99e24
--- /dev/null
+++ b/libs/binder/ProcessState.cpp
@@ -0,0 +1,398 @@
+/*
+ * Copyright (C) 2005 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.
+ */
+
+#define LOG_TAG "ProcessState"
+
+#include <cutils/process_name.h>
+
+#include <utils/ProcessState.h>
+
+#include <utils/Atomic.h>
+#include <utils/BpBinder.h>
+#include <utils/IPCThreadState.h>
+#include <utils/Log.h>
+#include <utils/String8.h>
+#include <utils/IServiceManager.h>
+#include <utils/String8.h>
+#include <utils/threads.h>
+
+#include <private/binder/binder_module.h>
+#include <private/binder/Static.h>
+
+#include <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/ioctl.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+
+#define BINDER_VM_SIZE (1*1024*1024)
+
+static bool gSingleProcess = false;
+
+
+// ---------------------------------------------------------------------------
+
+namespace android {
+
+// Global variables
+int mArgC;
+const char* const* mArgV;
+int mArgLen;
+
+class PoolThread : public Thread
+{
+public:
+ PoolThread(bool isMain)
+ : mIsMain(isMain)
+ {
+ }
+
+protected:
+ virtual bool threadLoop()
+ {
+ IPCThreadState::self()->joinThreadPool(mIsMain);
+ return false;
+ }
+
+ const bool mIsMain;
+};
+
+sp<ProcessState> ProcessState::self()
+{
+ if (gProcess != NULL) return gProcess;
+
+ AutoMutex _l(gProcessMutex);
+ if (gProcess == NULL) gProcess = new ProcessState;
+ return gProcess;
+}
+
+void ProcessState::setSingleProcess(bool singleProcess)
+{
+ gSingleProcess = singleProcess;
+}
+
+
+void ProcessState::setContextObject(const sp<IBinder>& object)
+{
+ setContextObject(object, String16("default"));
+}
+
+sp<IBinder> ProcessState::getContextObject(const sp<IBinder>& caller)
+{
+ if (supportsProcesses()) {
+ return getStrongProxyForHandle(0);
+ } else {
+ return getContextObject(String16("default"), caller);
+ }
+}
+
+void ProcessState::setContextObject(const sp<IBinder>& object, const String16& name)
+{
+ AutoMutex _l(mLock);
+ mContexts.add(name, object);
+}
+
+sp<IBinder> ProcessState::getContextObject(const String16& name, const sp<IBinder>& caller)
+{
+ mLock.lock();
+ sp<IBinder> object(
+ mContexts.indexOfKey(name) >= 0 ? mContexts.valueFor(name) : NULL);
+ mLock.unlock();
+
+ //printf("Getting context object %s for %p\n", String8(name).string(), caller.get());
+
+ if (object != NULL) return object;
+
+ // Don't attempt to retrieve contexts if we manage them
+ if (mManagesContexts) {
+ LOGE("getContextObject(%s) failed, but we manage the contexts!\n",
+ String8(name).string());
+ return NULL;
+ }
+
+ IPCThreadState* ipc = IPCThreadState::self();
+ {
+ Parcel data, reply;
+ // no interface token on this magic transaction
+ data.writeString16(name);
+ data.writeStrongBinder(caller);
+ status_t result = ipc->transact(0 /*magic*/, 0, data, &reply, 0);
+ if (result == NO_ERROR) {
+ object = reply.readStrongBinder();
+ }
+ }
+
+ ipc->flushCommands();
+
+ if (object != NULL) setContextObject(object, name);
+ return object;
+}
+
+bool ProcessState::supportsProcesses() const
+{
+ return mDriverFD >= 0;
+}
+
+void ProcessState::startThreadPool()
+{
+ AutoMutex _l(mLock);
+ if (!mThreadPoolStarted) {
+ mThreadPoolStarted = true;
+ spawnPooledThread(true);
+ }
+}
+
+bool ProcessState::isContextManager(void) const
+{
+ return mManagesContexts;
+}
+
+bool ProcessState::becomeContextManager(context_check_func checkFunc, void* userData)
+{
+ if (!mManagesContexts) {
+ AutoMutex _l(mLock);
+ mBinderContextCheckFunc = checkFunc;
+ mBinderContextUserData = userData;
+ if (mDriverFD >= 0) {
+ int dummy = 0;
+#if defined(HAVE_ANDROID_OS)
+ status_t result = ioctl(mDriverFD, BINDER_SET_CONTEXT_MGR, &dummy);
+#else
+ status_t result = INVALID_OPERATION;
+#endif
+ if (result == 0) {
+ mManagesContexts = true;
+ } else if (result == -1) {
+ mBinderContextCheckFunc = NULL;
+ mBinderContextUserData = NULL;
+ LOGE("Binder ioctl to become context manager failed: %s\n", strerror(errno));
+ }
+ } else {
+ // If there is no driver, our only world is the local
+ // process so we can always become the context manager there.
+ mManagesContexts = true;
+ }
+ }
+ return mManagesContexts;
+}
+
+ProcessState::handle_entry* ProcessState::lookupHandleLocked(int32_t handle)
+{
+ const size_t N=mHandleToObject.size();
+ if (N <= (size_t)handle) {
+ handle_entry e;
+ e.binder = NULL;
+ e.refs = NULL;
+ status_t err = mHandleToObject.insertAt(e, N, handle+1-N);
+ if (err < NO_ERROR) return NULL;
+ }
+ return &mHandleToObject.editItemAt(handle);
+}
+
+sp<IBinder> ProcessState::getStrongProxyForHandle(int32_t handle)
+{
+ sp<IBinder> result;
+
+ AutoMutex _l(mLock);
+
+ handle_entry* e = lookupHandleLocked(handle);
+
+ if (e != NULL) {
+ // We need to create a new BpBinder if there isn't currently one, OR we
+ // are unable to acquire a weak reference on this current one. See comment
+ // in getWeakProxyForHandle() for more info about this.
+ IBinder* b = e->binder;
+ if (b == NULL || !e->refs->attemptIncWeak(this)) {
+ b = new BpBinder(handle);
+ e->binder = b;
+ if (b) e->refs = b->getWeakRefs();
+ result = b;
+ } else {
+ // This little bit of nastyness is to allow us to add a primary
+ // reference to the remote proxy when this team doesn't have one
+ // but another team is sending the handle to us.
+ result.force_set(b);
+ e->refs->decWeak(this);
+ }
+ }
+
+ return result;
+}
+
+wp<IBinder> ProcessState::getWeakProxyForHandle(int32_t handle)
+{
+ wp<IBinder> result;
+
+ AutoMutex _l(mLock);
+
+ handle_entry* e = lookupHandleLocked(handle);
+
+ if (e != NULL) {
+ // We need to create a new BpBinder if there isn't currently one, OR we
+ // are unable to acquire a weak reference on this current one. The
+ // attemptIncWeak() is safe because we know the BpBinder destructor will always
+ // call expungeHandle(), which acquires the same lock we are holding now.
+ // We need to do this because there is a race condition between someone
+ // releasing a reference on this BpBinder, and a new reference on its handle
+ // arriving from the driver.
+ IBinder* b = e->binder;
+ if (b == NULL || !e->refs->attemptIncWeak(this)) {
+ b = new BpBinder(handle);
+ result = b;
+ e->binder = b;
+ if (b) e->refs = b->getWeakRefs();
+ } else {
+ result = b;
+ e->refs->decWeak(this);
+ }
+ }
+
+ return result;
+}
+
+void ProcessState::expungeHandle(int32_t handle, IBinder* binder)
+{
+ AutoMutex _l(mLock);
+
+ handle_entry* e = lookupHandleLocked(handle);
+
+ // This handle may have already been replaced with a new BpBinder
+ // (if someone failed the AttemptIncWeak() above); we don't want
+ // to overwrite it.
+ if (e && e->binder == binder) e->binder = NULL;
+}
+
+void ProcessState::setArgs(int argc, const char* const argv[])
+{
+ mArgC = argc;
+ mArgV = (const char **)argv;
+
+ mArgLen = 0;
+ for (int i=0; i<argc; i++) {
+ mArgLen += strlen(argv[i]) + 1;
+ }
+ mArgLen--;
+}
+
+int ProcessState::getArgC() const
+{
+ return mArgC;
+}
+
+const char* const* ProcessState::getArgV() const
+{
+ return mArgV;
+}
+
+void ProcessState::setArgV0(const char* txt)
+{
+ if (mArgV != NULL) {
+ strncpy((char*)mArgV[0], txt, mArgLen);
+ set_process_name(txt);
+ }
+}
+
+void ProcessState::spawnPooledThread(bool isMain)
+{
+ if (mThreadPoolStarted) {
+ int32_t s = android_atomic_add(1, &mThreadPoolSeq);
+ char buf[32];
+ sprintf(buf, "Binder Thread #%d", s);
+ LOGV("Spawning new pooled thread, name=%s\n", buf);
+ sp<Thread> t = new PoolThread(isMain);
+ t->run(buf);
+ }
+}
+
+static int open_driver()
+{
+ if (gSingleProcess) {
+ return -1;
+ }
+
+ int fd = open("/dev/binder", O_RDWR);
+ if (fd >= 0) {
+ fcntl(fd, F_SETFD, FD_CLOEXEC);
+ int vers;
+#if defined(HAVE_ANDROID_OS)
+ status_t result = ioctl(fd, BINDER_VERSION, &vers);
+#else
+ status_t result = -1;
+ errno = EPERM;
+#endif
+ if (result == -1) {
+ LOGE("Binder ioctl to obtain version failed: %s", strerror(errno));
+ close(fd);
+ fd = -1;
+ }
+ if (result != 0 || vers != BINDER_CURRENT_PROTOCOL_VERSION) {
+ LOGE("Binder driver protocol does not match user space protocol!");
+ close(fd);
+ fd = -1;
+ }
+#if defined(HAVE_ANDROID_OS)
+ size_t maxThreads = 15;
+ result = ioctl(fd, BINDER_SET_MAX_THREADS, &maxThreads);
+ if (result == -1) {
+ LOGE("Binder ioctl to set max threads failed: %s", strerror(errno));
+ }
+#endif
+
+ } else {
+ LOGW("Opening '/dev/binder' failed: %s\n", strerror(errno));
+ }
+ return fd;
+}
+
+ProcessState::ProcessState()
+ : mDriverFD(open_driver())
+ , mVMStart(MAP_FAILED)
+ , mManagesContexts(false)
+ , mBinderContextCheckFunc(NULL)
+ , mBinderContextUserData(NULL)
+ , mThreadPoolStarted(false)
+ , mThreadPoolSeq(1)
+{
+ if (mDriverFD >= 0) {
+ // XXX Ideally, there should be a specific define for whether we
+ // have mmap (or whether we could possibly have the kernel module
+ // availabla).
+#if !defined(HAVE_WIN32_IPC)
+ // mmap the binder, providing a chunk of virtual address space to receive transactions.
+ mVMStart = mmap(0, BINDER_VM_SIZE, PROT_READ, MAP_PRIVATE | MAP_NORESERVE, mDriverFD, 0);
+ if (mVMStart == MAP_FAILED) {
+ // *sigh*
+ LOGE("Using /dev/binder failed: unable to mmap transaction memory.\n");
+ close(mDriverFD);
+ mDriverFD = -1;
+ }
+#else
+ mDriverFD = -1;
+#endif
+ }
+ if (mDriverFD < 0) {
+ // Need to run without the driver, starting our own thread pool.
+ }
+}
+
+ProcessState::~ProcessState()
+{
+}
+
+}; // namespace android