/* * Copyright (C) 2010 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_NDEBUG 0 #define LOG_TAG "ALooperRoster" #include #include "ALooperRoster.h" #include "ADebug.h" #include "AHandler.h" #include "AMessage.h" namespace android { ALooperRoster::ALooperRoster() : mNextHandlerID(1), mNextReplyID(1) { } ALooper::handler_id ALooperRoster::registerHandler( const sp looper, const sp &handler) { Mutex::Autolock autoLock(mLock); if (handler->id() != 0) { CHECK(!"A handler must only be registered once."); return INVALID_OPERATION; } HandlerInfo info; info.mLooper = looper; info.mHandler = handler; ALooper::handler_id handlerID = mNextHandlerID++; mHandlers.add(handlerID, info); handler->setID(handlerID); return handlerID; } void ALooperRoster::unregisterHandler(ALooper::handler_id handlerID) { Mutex::Autolock autoLock(mLock); ssize_t index = mHandlers.indexOfKey(handlerID); if (index < 0) { return; } const HandlerInfo &info = mHandlers.valueAt(index); sp handler = info.mHandler.promote(); if (handler != NULL) { handler->setID(0); } mHandlers.removeItemsAt(index); } void ALooperRoster::unregisterStaleHandlers() { Vector > activeLoopers; { Mutex::Autolock autoLock(mLock); for (size_t i = mHandlers.size(); i-- > 0;) { const HandlerInfo &info = mHandlers.valueAt(i); sp looper = info.mLooper.promote(); if (looper == NULL) { ALOGV("Unregistering stale handler %d", mHandlers.keyAt(i)); mHandlers.removeItemsAt(i); } else { // At this point 'looper' might be the only sp<> keeping // the object alive. To prevent it from going out of scope // and having ~ALooper call this method again recursively // and then deadlocking because of the Autolock above, add // it to a Vector which will go out of scope after the lock // has been released. activeLoopers.add(looper); } } } } status_t ALooperRoster::postMessage( const sp &msg, int64_t delayUs) { sp looper = findLooper(msg->target()); if (looper == NULL) { return -ENOENT; } looper->post(msg, delayUs); return OK; } void ALooperRoster::deliverMessage(const sp &msg) { sp handler; { Mutex::Autolock autoLock(mLock); ssize_t index = mHandlers.indexOfKey(msg->target()); if (index < 0) { ALOGW("failed to deliver message. Target handler not registered."); return; } const HandlerInfo &info = mHandlers.valueAt(index); handler = info.mHandler.promote(); if (handler == NULL) { ALOGW("failed to deliver message. " "Target handler %d registered, but object gone.", msg->target()); mHandlers.removeItemsAt(index); return; } } handler->onMessageReceived(msg); } sp ALooperRoster::findLooper(ALooper::handler_id handlerID) { Mutex::Autolock autoLock(mLock); ssize_t index = mHandlers.indexOfKey(handlerID); if (index < 0) { return NULL; } sp looper = mHandlers.valueAt(index).mLooper.promote(); if (looper == NULL) { mHandlers.removeItemsAt(index); return NULL; } return looper; } status_t ALooperRoster::postAndAwaitResponse( const sp &msg, sp *response) { sp looper = findLooper(msg->target()); if (looper == NULL) { ALOGW("failed to post message. " "Target handler %d still registered, but object gone.", msg->target()); response->clear(); return -ENOENT; } Mutex::Autolock autoLock(mLock); uint32_t replyID = mNextReplyID++; msg->setInt32("replyID", replyID); looper->post(msg, 0 /* delayUs */); ssize_t index; while ((index = mReplies.indexOfKey(replyID)) < 0) { mRepliesCondition.wait(mLock); } *response = mReplies.valueAt(index); mReplies.removeItemsAt(index); return OK; } void ALooperRoster::postReply(uint32_t replyID, const sp &reply) { Mutex::Autolock autoLock(mLock); CHECK(mReplies.indexOfKey(replyID) < 0); mReplies.add(replyID, reply); mRepliesCondition.broadcast(); } } // namespace android