/* * 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. */ #include "AMessage.h" #include #include "AAtomizer.h" #include "ABuffer.h" #include "ADebug.h" #include "ALooperRoster.h" #include "AString.h" #include #include namespace android { extern ALooperRoster gLooperRoster; AMessage::AMessage(uint32_t what, ALooper::handler_id target) : mWhat(what), mTarget(target), mNumItems(0) { } AMessage::~AMessage() { clear(); } void AMessage::setWhat(uint32_t what) { mWhat = what; } uint32_t AMessage::what() const { return mWhat; } void AMessage::setTarget(ALooper::handler_id handlerID) { mTarget = handlerID; } ALooper::handler_id AMessage::target() const { return mTarget; } void AMessage::clear() { for (size_t i = 0; i < mNumItems; ++i) { Item *item = &mItems[i]; freeItem(item); } mNumItems = 0; } void AMessage::freeItem(Item *item) { switch (item->mType) { case kTypeString: { delete item->u.stringValue; break; } case kTypeObject: case kTypeMessage: case kTypeBuffer: { if (item->u.refValue != NULL) { item->u.refValue->decStrong(this); } break; } default: break; } } AMessage::Item *AMessage::allocateItem(const char *name) { name = AAtomizer::Atomize(name); size_t i = 0; while (i < mNumItems && mItems[i].mName != name) { ++i; } Item *item; if (i < mNumItems) { item = &mItems[i]; freeItem(item); } else { CHECK(mNumItems < kMaxNumItems); i = mNumItems++; item = &mItems[i]; item->mName = name; } return item; } const AMessage::Item *AMessage::findItem( const char *name, Type type) const { name = AAtomizer::Atomize(name); for (size_t i = 0; i < mNumItems; ++i) { const Item *item = &mItems[i]; if (item->mName == name) { return item->mType == type ? item : NULL; } } return NULL; } #define BASIC_TYPE(NAME,FIELDNAME,TYPENAME) \ void AMessage::set##NAME(const char *name, TYPENAME value) { \ Item *item = allocateItem(name); \ \ item->mType = kType##NAME; \ item->u.FIELDNAME = value; \ } \ \ bool AMessage::find##NAME(const char *name, TYPENAME *value) const { \ const Item *item = findItem(name, kType##NAME); \ if (item) { \ *value = item->u.FIELDNAME; \ return true; \ } \ return false; \ } BASIC_TYPE(Int32,int32Value,int32_t) BASIC_TYPE(Int64,int64Value,int64_t) BASIC_TYPE(Size,sizeValue,size_t) BASIC_TYPE(Float,floatValue,float) BASIC_TYPE(Double,doubleValue,double) BASIC_TYPE(Pointer,ptrValue,void *) #undef BASIC_TYPE void AMessage::setString( const char *name, const char *s, ssize_t len) { Item *item = allocateItem(name); item->mType = kTypeString; item->u.stringValue = new AString(s, len < 0 ? strlen(s) : len); } void AMessage::setObjectInternal( const char *name, const sp &obj, Type type) { Item *item = allocateItem(name); item->mType = type; if (obj != NULL) { obj->incStrong(this); } item->u.refValue = obj.get(); } void AMessage::setObject(const char *name, const sp &obj) { setObjectInternal(name, obj, kTypeObject); } void AMessage::setBuffer(const char *name, const sp &buffer) { setObjectInternal(name, sp(buffer), kTypeBuffer); } void AMessage::setMessage(const char *name, const sp &obj) { Item *item = allocateItem(name); item->mType = kTypeMessage; if (obj != NULL) { obj->incStrong(this); } item->u.refValue = obj.get(); } void AMessage::setRect( const char *name, int32_t left, int32_t top, int32_t right, int32_t bottom) { Item *item = allocateItem(name); item->mType = kTypeRect; item->u.rectValue.mLeft = left; item->u.rectValue.mTop = top; item->u.rectValue.mRight = right; item->u.rectValue.mBottom = bottom; } bool AMessage::findString(const char *name, AString *value) const { const Item *item = findItem(name, kTypeString); if (item) { *value = *item->u.stringValue; return true; } return false; } bool AMessage::findObject(const char *name, sp *obj) const { const Item *item = findItem(name, kTypeObject); if (item) { *obj = item->u.refValue; return true; } return false; } bool AMessage::findBuffer(const char *name, sp *buf) const { const Item *item = findItem(name, kTypeBuffer); if (item) { *buf = (ABuffer *)(item->u.refValue); return true; } return false; } bool AMessage::findMessage(const char *name, sp *obj) const { const Item *item = findItem(name, kTypeMessage); if (item) { *obj = static_cast(item->u.refValue); return true; } return false; } bool AMessage::findRect( const char *name, int32_t *left, int32_t *top, int32_t *right, int32_t *bottom) const { const Item *item = findItem(name, kTypeRect); if (item == NULL) { return false; } *left = item->u.rectValue.mLeft; *top = item->u.rectValue.mTop; *right = item->u.rectValue.mRight; *bottom = item->u.rectValue.mBottom; return true; } void AMessage::post(int64_t delayUs) { gLooperRoster.postMessage(this, delayUs); } status_t AMessage::postAndAwaitResponse(sp *response) { return gLooperRoster.postAndAwaitResponse(this, response); } void AMessage::postReply(uint32_t replyID) { gLooperRoster.postReply(replyID, this); } bool AMessage::senderAwaitsResponse(uint32_t *replyID) const { int32_t tmp; bool found = findInt32("replyID", &tmp); if (!found) { return false; } *replyID = static_cast(tmp); return true; } sp AMessage::dup() const { sp msg = new AMessage(mWhat, mTarget); msg->mNumItems = mNumItems; for (size_t i = 0; i < mNumItems; ++i) { const Item *from = &mItems[i]; Item *to = &msg->mItems[i]; to->mName = from->mName; to->mType = from->mType; switch (from->mType) { case kTypeString: { to->u.stringValue = new AString(*from->u.stringValue); break; } case kTypeObject: case kTypeBuffer: { to->u.refValue = from->u.refValue; to->u.refValue->incStrong(msg.get()); break; } case kTypeMessage: { sp copy = static_cast(from->u.refValue)->dup(); to->u.refValue = copy.get(); to->u.refValue->incStrong(msg.get()); break; } default: { to->u = from->u; break; } } } return msg; } static void appendIndent(AString *s, int32_t indent) { static const char kWhitespace[] = " " " "; CHECK_LT((size_t)indent, sizeof(kWhitespace)); s->append(kWhitespace, indent); } static bool isFourcc(uint32_t what) { return isprint(what & 0xff) && isprint((what >> 8) & 0xff) && isprint((what >> 16) & 0xff) && isprint((what >> 24) & 0xff); } AString AMessage::debugString(int32_t indent) const { AString s = "AMessage(what = "; AString tmp; if (isFourcc(mWhat)) { tmp = StringPrintf( "'%c%c%c%c'", (char)(mWhat >> 24), (char)((mWhat >> 16) & 0xff), (char)((mWhat >> 8) & 0xff), (char)(mWhat & 0xff)); } else { tmp = StringPrintf("0x%08x", mWhat); } s.append(tmp); if (mTarget != 0) { tmp = StringPrintf(", target = %d", mTarget); s.append(tmp); } s.append(") = {\n"); for (size_t i = 0; i < mNumItems; ++i) { const Item &item = mItems[i]; switch (item.mType) { case kTypeInt32: tmp = StringPrintf( "int32_t %s = %d", item.mName, item.u.int32Value); break; case kTypeInt64: tmp = StringPrintf( "int64_t %s = %lld", item.mName, item.u.int64Value); break; case kTypeSize: tmp = StringPrintf( "size_t %s = %d", item.mName, item.u.sizeValue); break; case kTypeFloat: tmp = StringPrintf( "float %s = %f", item.mName, item.u.floatValue); break; case kTypeDouble: tmp = StringPrintf( "double %s = %f", item.mName, item.u.doubleValue); break; case kTypePointer: tmp = StringPrintf( "void *%s = %p", item.mName, item.u.ptrValue); break; case kTypeString: tmp = StringPrintf( "string %s = \"%s\"", item.mName, item.u.stringValue->c_str()); break; case kTypeObject: tmp = StringPrintf( "RefBase *%s = %p", item.mName, item.u.refValue); break; case kTypeBuffer: { sp buffer = static_cast(item.u.refValue); if (buffer != NULL && buffer->size() <= 64) { tmp = StringPrintf("Buffer %s = {\n", item.mName); hexdump(buffer->data(), buffer->size(), indent + 4, &tmp); appendIndent(&tmp, indent + 2); tmp.append("}"); } else { tmp = StringPrintf( "Buffer *%s = %p", item.mName, buffer.get()); } break; } case kTypeMessage: tmp = StringPrintf( "AMessage %s = %s", item.mName, static_cast( item.u.refValue)->debugString( indent + strlen(item.mName) + 14).c_str()); break; case kTypeRect: tmp = StringPrintf( "Rect %s(%d, %d, %d, %d)", item.mName, item.u.rectValue.mLeft, item.u.rectValue.mTop, item.u.rectValue.mRight, item.u.rectValue.mBottom); break; default: TRESPASS(); } appendIndent(&s, indent); s.append(" "); s.append(tmp); s.append("\n"); } appendIndent(&s, indent); s.append("}"); return s; } // static sp AMessage::FromParcel(const Parcel &parcel) { int32_t what = parcel.readInt32(); sp msg = new AMessage(what); msg->mNumItems = static_cast(parcel.readInt32()); for (size_t i = 0; i < msg->mNumItems; ++i) { Item *item = &msg->mItems[i]; item->mName = AAtomizer::Atomize(parcel.readCString()); item->mType = static_cast(parcel.readInt32()); switch (item->mType) { case kTypeInt32: { item->u.int32Value = parcel.readInt32(); break; } case kTypeInt64: { item->u.int64Value = parcel.readInt64(); break; } case kTypeSize: { item->u.sizeValue = static_cast(parcel.readInt32()); break; } case kTypeFloat: { item->u.floatValue = parcel.readFloat(); break; } case kTypeDouble: { item->u.doubleValue = parcel.readDouble(); break; } case kTypeString: { item->u.stringValue = new AString(parcel.readCString()); break; } case kTypeMessage: { sp subMsg = AMessage::FromParcel(parcel); subMsg->incStrong(msg.get()); item->u.refValue = subMsg.get(); break; } default: { ALOGE("This type of object cannot cross process boundaries."); TRESPASS(); } } } return msg; } void AMessage::writeToParcel(Parcel *parcel) const { parcel->writeInt32(static_cast(mWhat)); parcel->writeInt32(static_cast(mNumItems)); for (size_t i = 0; i < mNumItems; ++i) { const Item &item = mItems[i]; parcel->writeCString(item.mName); parcel->writeInt32(static_cast(item.mType)); switch (item.mType) { case kTypeInt32: { parcel->writeInt32(item.u.int32Value); break; } case kTypeInt64: { parcel->writeInt64(item.u.int64Value); break; } case kTypeSize: { parcel->writeInt32(static_cast(item.u.sizeValue)); break; } case kTypeFloat: { parcel->writeFloat(item.u.floatValue); break; } case kTypeDouble: { parcel->writeDouble(item.u.doubleValue); break; } case kTypeString: { parcel->writeCString(item.u.stringValue->c_str()); break; } case kTypeMessage: { static_cast(item.u.refValue)->writeToParcel(parcel); break; } default: { ALOGE("This type of object cannot cross process boundaries."); TRESPASS(); } } } } size_t AMessage::countEntries() const { return mNumItems; } const char *AMessage::getEntryNameAt(size_t index, Type *type) const { if (index >= mNumItems) { *type = kTypeInt32; return NULL; } *type = mItems[index].mType; return mItems[index].mName; } } // namespace android