1153 lines
33 KiB
C++
1153 lines
33 KiB
C++
/*
|
|
* 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_TAG "AMessage"
|
|
//#define LOG_NDEBUG 0
|
|
//#define DUMP_STATS
|
|
|
|
#include <ctype.h>
|
|
|
|
#include "AMessage.h"
|
|
|
|
#include <log/log.h>
|
|
|
|
#include "AAtomizer.h"
|
|
#include "ABuffer.h"
|
|
#include "ADebug.h"
|
|
#include "ALooperRoster.h"
|
|
#include "AHandler.h"
|
|
#include "AString.h"
|
|
|
|
#include <media/stagefright/foundation/hexdump.h>
|
|
|
|
#if defined(__ANDROID__) && !defined(__ANDROID_VNDK__) && !defined(__ANDROID_APEX__)
|
|
#include <binder/Parcel.h>
|
|
#endif
|
|
|
|
namespace android {
|
|
|
|
extern ALooperRoster gLooperRoster;
|
|
|
|
status_t AReplyToken::setReply(const sp<AMessage> &reply) {
|
|
if (mReplied) {
|
|
ALOGE("trying to post a duplicate reply");
|
|
return -EBUSY;
|
|
}
|
|
CHECK(mReply == NULL);
|
|
mReply = reply;
|
|
mReplied = true;
|
|
return OK;
|
|
}
|
|
|
|
AMessage::AMessage(void)
|
|
: mWhat(0),
|
|
mTarget(0) {
|
|
}
|
|
|
|
AMessage::AMessage(uint32_t what, const sp<const AHandler> &handler)
|
|
: mWhat(what) {
|
|
setTarget(handler);
|
|
}
|
|
|
|
AMessage::~AMessage() {
|
|
clear();
|
|
}
|
|
|
|
void AMessage::setWhat(uint32_t what) {
|
|
mWhat = what;
|
|
}
|
|
|
|
uint32_t AMessage::what() const {
|
|
return mWhat;
|
|
}
|
|
|
|
void AMessage::setTarget(const sp<const AHandler> &handler) {
|
|
if (handler == NULL) {
|
|
mTarget = 0;
|
|
mHandler.clear();
|
|
mLooper.clear();
|
|
} else {
|
|
mTarget = handler->id();
|
|
mHandler = handler->getHandler();
|
|
mLooper = handler->getLooper();
|
|
}
|
|
}
|
|
|
|
void AMessage::clear() {
|
|
// Item needs to be handled delicately
|
|
for (Item &item : mItems) {
|
|
delete[] item.mName;
|
|
item.mName = NULL;
|
|
freeItemValue(&item);
|
|
}
|
|
mItems.clear();
|
|
}
|
|
|
|
void AMessage::freeItemValue(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;
|
|
}
|
|
item->mType = kTypeInt32; // clear type
|
|
}
|
|
|
|
#ifdef DUMP_STATS
|
|
#include <utils/Mutex.h>
|
|
|
|
Mutex gLock;
|
|
static int32_t gFindItemCalls = 1;
|
|
static int32_t gDupCalls = 1;
|
|
static int32_t gAverageNumItems = 0;
|
|
static int32_t gAverageNumChecks = 0;
|
|
static int32_t gAverageNumMemChecks = 0;
|
|
static int32_t gAverageDupItems = 0;
|
|
static int32_t gLastChecked = -1;
|
|
|
|
static void reportStats() {
|
|
int32_t time = (ALooper::GetNowUs() / 1000);
|
|
if (time / 1000 != gLastChecked / 1000) {
|
|
gLastChecked = time;
|
|
ALOGI("called findItemIx %zu times (for len=%.1f i=%.1f/%.1f mem) dup %zu times (for len=%.1f)",
|
|
gFindItemCalls,
|
|
gAverageNumItems / (float)gFindItemCalls,
|
|
gAverageNumChecks / (float)gFindItemCalls,
|
|
gAverageNumMemChecks / (float)gFindItemCalls,
|
|
gDupCalls,
|
|
gAverageDupItems / (float)gDupCalls);
|
|
gFindItemCalls = gDupCalls = 1;
|
|
gAverageNumItems = gAverageNumChecks = gAverageNumMemChecks = gAverageDupItems = 0;
|
|
gLastChecked = time;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
inline size_t AMessage::findItemIndex(const char *name, size_t len) const {
|
|
#ifdef DUMP_STATS
|
|
size_t memchecks = 0;
|
|
#endif
|
|
size_t i = 0;
|
|
for (; i < mItems.size(); i++) {
|
|
if (len != mItems[i].mNameLength) {
|
|
continue;
|
|
}
|
|
#ifdef DUMP_STATS
|
|
++memchecks;
|
|
#endif
|
|
if (!memcmp(mItems[i].mName, name, len)) {
|
|
break;
|
|
}
|
|
}
|
|
#ifdef DUMP_STATS
|
|
{
|
|
Mutex::Autolock _l(gLock);
|
|
++gFindItemCalls;
|
|
gAverageNumItems += mItems.size();
|
|
gAverageNumMemChecks += memchecks;
|
|
gAverageNumChecks += i;
|
|
reportStats();
|
|
}
|
|
#endif
|
|
return i;
|
|
}
|
|
|
|
// assumes item's name was uninitialized or NULL
|
|
void AMessage::Item::setName(const char *name, size_t len) {
|
|
mNameLength = len;
|
|
mName = new char[len + 1];
|
|
memcpy((void*)mName, name, len + 1);
|
|
}
|
|
|
|
AMessage::Item::Item(const char *name, size_t len)
|
|
: mType(kTypeInt32) {
|
|
// mName and mNameLength are initialized by setName
|
|
setName(name, len);
|
|
}
|
|
|
|
AMessage::Item *AMessage::allocateItem(const char *name) {
|
|
size_t len = strlen(name);
|
|
size_t i = findItemIndex(name, len);
|
|
Item *item;
|
|
|
|
if (i < mItems.size()) {
|
|
item = &mItems[i];
|
|
freeItemValue(item);
|
|
} else {
|
|
CHECK(mItems.size() < kMaxNumItems);
|
|
i = mItems.size();
|
|
// place a 'blank' item at the end - this is of type kTypeInt32
|
|
mItems.emplace_back(name, len);
|
|
item = &mItems[i];
|
|
}
|
|
|
|
return item;
|
|
}
|
|
|
|
const AMessage::Item *AMessage::findItem(
|
|
const char *name, Type type) const {
|
|
size_t i = findItemIndex(name, strlen(name));
|
|
if (i < mItems.size()) {
|
|
const Item *item = &mItems[i];
|
|
return item->mType == type ? item : NULL;
|
|
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
bool AMessage::findAsFloat(const char *name, float *value) const {
|
|
size_t i = findItemIndex(name, strlen(name));
|
|
if (i < mItems.size()) {
|
|
const Item *item = &mItems[i];
|
|
switch (item->mType) {
|
|
case kTypeFloat:
|
|
*value = item->u.floatValue;
|
|
return true;
|
|
case kTypeDouble:
|
|
*value = (float)item->u.doubleValue;
|
|
return true;
|
|
case kTypeInt64:
|
|
*value = (float)item->u.int64Value;
|
|
return true;
|
|
case kTypeInt32:
|
|
*value = (float)item->u.int32Value;
|
|
return true;
|
|
case kTypeSize:
|
|
*value = (float)item->u.sizeValue;
|
|
return true;
|
|
default:
|
|
return false;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool AMessage::findAsInt64(const char *name, int64_t *value) const {
|
|
size_t i = findItemIndex(name, strlen(name));
|
|
if (i < mItems.size()) {
|
|
const Item *item = &mItems[i];
|
|
switch (item->mType) {
|
|
case kTypeInt64:
|
|
*value = item->u.int64Value;
|
|
return true;
|
|
case kTypeInt32:
|
|
*value = item->u.int32Value;
|
|
return true;
|
|
default:
|
|
return false;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool AMessage::contains(const char *name) const {
|
|
size_t i = findItemIndex(name, strlen(name));
|
|
return i < mItems.size();
|
|
}
|
|
|
|
#define BASIC_TYPE(NAME,FIELDNAME,TYPENAME) \
|
|
void AMessage::set##NAME(const char *name, TYPENAME value) { \
|
|
Item *item = allocateItem(name); \
|
|
if (item) { \
|
|
item->mType = kType##NAME; \
|
|
item->u.FIELDNAME = value; \
|
|
} \
|
|
} \
|
|
\
|
|
/* NOLINT added to avoid incorrect warning/fix from clang.tidy */ \
|
|
bool AMessage::find##NAME(const char *name, TYPENAME *value) const { /* NOLINT */ \
|
|
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);
|
|
if (item) {
|
|
item->mType = kTypeString;
|
|
item->u.stringValue = new AString(s, len < 0 ? strlen(s) : len);
|
|
}
|
|
}
|
|
|
|
void AMessage::setString(
|
|
const char *name, const AString &s) {
|
|
setString(name, s.c_str(), s.size());
|
|
}
|
|
|
|
void AMessage::setObjectInternal(
|
|
const char *name, const sp<RefBase> &obj, Type type) {
|
|
Item *item = allocateItem(name);
|
|
if (item) {
|
|
item->mType = type;
|
|
|
|
if (obj != NULL) { obj->incStrong(this); }
|
|
item->u.refValue = obj.get();
|
|
}
|
|
}
|
|
|
|
void AMessage::setObject(const char *name, const sp<RefBase> &obj) {
|
|
setObjectInternal(name, obj, kTypeObject);
|
|
}
|
|
|
|
void AMessage::setBuffer(const char *name, const sp<ABuffer> &buffer) {
|
|
setObjectInternal(name, sp<RefBase>(buffer), kTypeBuffer);
|
|
}
|
|
|
|
void AMessage::setMessage(const char *name, const sp<AMessage> &obj) {
|
|
Item *item = allocateItem(name);
|
|
if (item) {
|
|
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);
|
|
if (item) {
|
|
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<RefBase> *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<ABuffer> *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<AMessage> *obj) const {
|
|
const Item *item = findItem(name, kTypeMessage);
|
|
if (item) {
|
|
*obj = static_cast<AMessage *>(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::deliver() {
|
|
sp<AHandler> handler = mHandler.promote();
|
|
if (handler == NULL) {
|
|
ALOGW("failed to deliver message as target handler %d is gone.", mTarget);
|
|
return;
|
|
}
|
|
|
|
handler->deliverMessage(this);
|
|
}
|
|
|
|
status_t AMessage::post(int64_t delayUs) {
|
|
sp<ALooper> looper = mLooper.promote();
|
|
if (looper == NULL) {
|
|
ALOGW("failed to post message as target looper for handler %d is gone.", mTarget);
|
|
return -ENOENT;
|
|
}
|
|
|
|
looper->post(this, delayUs);
|
|
return OK;
|
|
}
|
|
|
|
status_t AMessage::postUnique(const sp<RefBase> &token, int64_t delayUs) {
|
|
sp<ALooper> looper = mLooper.promote();
|
|
if (looper == NULL) {
|
|
ALOGW("failed to post message as target looper for handler %d is gone.",
|
|
mTarget);
|
|
return -ENOENT;
|
|
}
|
|
|
|
return looper->postUnique(this, token, delayUs);
|
|
}
|
|
|
|
status_t AMessage::postAndAwaitResponse(sp<AMessage> *response) {
|
|
sp<ALooper> looper = mLooper.promote();
|
|
if (looper == NULL) {
|
|
ALOGW("failed to post message as target looper for handler %d is gone.", mTarget);
|
|
return -ENOENT;
|
|
}
|
|
|
|
sp<AReplyToken> token = looper->createReplyToken();
|
|
if (token == NULL) {
|
|
ALOGE("failed to create reply token");
|
|
return -ENOMEM;
|
|
}
|
|
setObject("replyID", token);
|
|
|
|
looper->post(this, 0 /* delayUs */);
|
|
return looper->awaitResponse(token, response);
|
|
}
|
|
|
|
status_t AMessage::postReply(const sp<AReplyToken> &replyToken) {
|
|
if (replyToken == NULL) {
|
|
ALOGW("failed to post reply to a NULL token");
|
|
return -ENOENT;
|
|
}
|
|
sp<ALooper> looper = replyToken->getLooper();
|
|
if (looper == NULL) {
|
|
ALOGW("failed to post reply as target looper is gone.");
|
|
return -ENOENT;
|
|
}
|
|
return looper->postReply(replyToken, this);
|
|
}
|
|
|
|
bool AMessage::senderAwaitsResponse(sp<AReplyToken> *replyToken) {
|
|
sp<RefBase> tmp;
|
|
bool found = findObject("replyID", &tmp);
|
|
|
|
if (!found) {
|
|
return false;
|
|
}
|
|
|
|
*replyToken = static_cast<AReplyToken *>(tmp.get());
|
|
tmp.clear();
|
|
setObject("replyID", tmp);
|
|
// TODO: delete Object instead of setting it to NULL
|
|
|
|
return *replyToken != NULL;
|
|
}
|
|
|
|
sp<AMessage> AMessage::dup() const {
|
|
sp<AMessage> msg = new AMessage(mWhat, mHandler.promote());
|
|
msg->mItems = mItems;
|
|
|
|
#ifdef DUMP_STATS
|
|
{
|
|
Mutex::Autolock _l(gLock);
|
|
++gDupCalls;
|
|
gAverageDupItems += mItems.size();
|
|
reportStats();
|
|
}
|
|
#endif
|
|
|
|
for (size_t i = 0; i < mItems.size(); ++i) {
|
|
const Item *from = &mItems[i];
|
|
Item *to = &msg->mItems[i];
|
|
|
|
to->setName(from->mName, from->mNameLength);
|
|
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<AMessage> copy =
|
|
static_cast<AMessage *>(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 = AStringPrintf(
|
|
"'%c%c%c%c'",
|
|
(char)(mWhat >> 24),
|
|
(char)((mWhat >> 16) & 0xff),
|
|
(char)((mWhat >> 8) & 0xff),
|
|
(char)(mWhat & 0xff));
|
|
} else {
|
|
tmp = AStringPrintf("0x%08x", mWhat);
|
|
}
|
|
s.append(tmp);
|
|
|
|
if (mTarget != 0) {
|
|
tmp = AStringPrintf(", target = %d", mTarget);
|
|
s.append(tmp);
|
|
}
|
|
s.append(") = {\n");
|
|
|
|
for (size_t i = 0; i < mItems.size(); ++i) {
|
|
const Item &item = mItems[i];
|
|
|
|
switch (item.mType) {
|
|
case kTypeInt32:
|
|
tmp = AStringPrintf(
|
|
"int32_t %s = %d", item.mName, item.u.int32Value);
|
|
break;
|
|
case kTypeInt64:
|
|
tmp = AStringPrintf(
|
|
"int64_t %s = %lld", item.mName, item.u.int64Value);
|
|
break;
|
|
case kTypeSize:
|
|
tmp = AStringPrintf(
|
|
"size_t %s = %d", item.mName, item.u.sizeValue);
|
|
break;
|
|
case kTypeFloat:
|
|
tmp = AStringPrintf(
|
|
"float %s = %f", item.mName, item.u.floatValue);
|
|
break;
|
|
case kTypeDouble:
|
|
tmp = AStringPrintf(
|
|
"double %s = %f", item.mName, item.u.doubleValue);
|
|
break;
|
|
case kTypePointer:
|
|
tmp = AStringPrintf(
|
|
"void *%s = %p", item.mName, item.u.ptrValue);
|
|
break;
|
|
case kTypeString:
|
|
tmp = AStringPrintf(
|
|
"string %s = \"%s\"",
|
|
item.mName,
|
|
item.u.stringValue->c_str());
|
|
break;
|
|
case kTypeObject:
|
|
tmp = AStringPrintf(
|
|
"RefBase *%s = %p", item.mName, item.u.refValue);
|
|
break;
|
|
case kTypeBuffer:
|
|
{
|
|
sp<ABuffer> buffer = static_cast<ABuffer *>(item.u.refValue);
|
|
|
|
if (buffer != NULL && buffer->data() != NULL && buffer->size() <= 64) {
|
|
tmp = AStringPrintf("Buffer %s = {\n", item.mName);
|
|
hexdump(buffer->data(), buffer->size(), indent + 4, &tmp);
|
|
appendIndent(&tmp, indent + 2);
|
|
tmp.append("}");
|
|
} else {
|
|
tmp = AStringPrintf(
|
|
"Buffer *%s = %p", item.mName, buffer.get());
|
|
}
|
|
break;
|
|
}
|
|
case kTypeMessage:
|
|
tmp = AStringPrintf(
|
|
"AMessage %s = %s",
|
|
item.mName,
|
|
static_cast<AMessage *>(
|
|
item.u.refValue)->debugString(
|
|
indent + strlen(item.mName) + 14).c_str());
|
|
break;
|
|
case kTypeRect:
|
|
tmp = AStringPrintf(
|
|
"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;
|
|
}
|
|
|
|
#if defined(__ANDROID__) && !defined(__ANDROID_VNDK__) && !defined(__ANDROID_APEX__)
|
|
// static
|
|
sp<AMessage> AMessage::FromParcel(const Parcel &parcel, size_t maxNestingLevel) {
|
|
int32_t what = parcel.readInt32();
|
|
sp<AMessage> msg = new AMessage();
|
|
msg->setWhat(what);
|
|
|
|
size_t numItems = static_cast<size_t>(parcel.readInt32());
|
|
if (numItems > kMaxNumItems) {
|
|
ALOGE("Too large number of items clipped.");
|
|
numItems = kMaxNumItems;
|
|
}
|
|
msg->mItems.resize(numItems);
|
|
|
|
for (size_t i = 0; i < msg->mItems.size(); ++i) {
|
|
Item *item = &msg->mItems[i];
|
|
|
|
const char *name = parcel.readCString();
|
|
if (name == NULL) {
|
|
ALOGE("Failed reading name for an item. Parsing aborted.");
|
|
msg->mItems.resize(i);
|
|
break;
|
|
}
|
|
|
|
item->mType = static_cast<Type>(parcel.readInt32());
|
|
// setName() happens below so that we don't leak memory when parsing
|
|
// is aborted in the middle.
|
|
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<size_t>(parcel.readInt32());
|
|
break;
|
|
}
|
|
|
|
case kTypeFloat:
|
|
{
|
|
item->u.floatValue = parcel.readFloat();
|
|
break;
|
|
}
|
|
|
|
case kTypeDouble:
|
|
{
|
|
item->u.doubleValue = parcel.readDouble();
|
|
break;
|
|
}
|
|
|
|
case kTypeString:
|
|
{
|
|
const char *stringValue = parcel.readCString();
|
|
if (stringValue == NULL) {
|
|
ALOGE("Failed reading string value from a parcel. "
|
|
"Parsing aborted.");
|
|
msg->mItems.resize(i);
|
|
continue;
|
|
// The loop will terminate subsequently.
|
|
} else {
|
|
item->u.stringValue = new AString(stringValue);
|
|
}
|
|
break;
|
|
}
|
|
|
|
case kTypeMessage:
|
|
{
|
|
if (maxNestingLevel == 0) {
|
|
ALOGE("Too many levels of AMessage nesting.");
|
|
return NULL;
|
|
}
|
|
sp<AMessage> subMsg = AMessage::FromParcel(
|
|
parcel,
|
|
maxNestingLevel - 1);
|
|
if (subMsg == NULL) {
|
|
// This condition will be triggered when there exists an
|
|
// object that cannot cross process boundaries or when the
|
|
// level of nested AMessage is too deep.
|
|
return NULL;
|
|
}
|
|
subMsg->incStrong(msg.get());
|
|
|
|
item->u.refValue = subMsg.get();
|
|
break;
|
|
}
|
|
|
|
default:
|
|
{
|
|
ALOGE("This type of object cannot cross process boundaries.");
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
item->setName(name, strlen(name));
|
|
}
|
|
|
|
return msg;
|
|
}
|
|
|
|
void AMessage::writeToParcel(Parcel *parcel) const {
|
|
parcel->writeInt32(static_cast<int32_t>(mWhat));
|
|
parcel->writeInt32(static_cast<int32_t>(mItems.size()));
|
|
|
|
for (const Item &item : mItems) {
|
|
parcel->writeCString(item.mName);
|
|
parcel->writeInt32(static_cast<int32_t>(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<int32_t>(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<AMessage *>(item.u.refValue)->writeToParcel(parcel);
|
|
break;
|
|
}
|
|
|
|
default:
|
|
{
|
|
ALOGE("This type of object cannot cross process boundaries.");
|
|
TRESPASS();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
#endif // defined(__ANDROID__) && !defined(__ANDROID_VNDK__) && !defined(__ANDROID_APEX__)
|
|
|
|
sp<AMessage> AMessage::changesFrom(const sp<const AMessage> &other, bool deep) const {
|
|
if (other == NULL) {
|
|
return const_cast<AMessage*>(this);
|
|
}
|
|
|
|
sp<AMessage> diff = new AMessage;
|
|
if (mWhat != other->mWhat) {
|
|
diff->setWhat(mWhat);
|
|
}
|
|
if (mHandler != other->mHandler) {
|
|
diff->setTarget(mHandler.promote());
|
|
}
|
|
|
|
for (const Item &item : mItems) {
|
|
const Item *oitem = other->findItem(item.mName, item.mType);
|
|
switch (item.mType) {
|
|
case kTypeInt32:
|
|
if (oitem == NULL || item.u.int32Value != oitem->u.int32Value) {
|
|
diff->setInt32(item.mName, item.u.int32Value);
|
|
}
|
|
break;
|
|
|
|
case kTypeInt64:
|
|
if (oitem == NULL || item.u.int64Value != oitem->u.int64Value) {
|
|
diff->setInt64(item.mName, item.u.int64Value);
|
|
}
|
|
break;
|
|
|
|
case kTypeSize:
|
|
if (oitem == NULL || item.u.sizeValue != oitem->u.sizeValue) {
|
|
diff->setSize(item.mName, item.u.sizeValue);
|
|
}
|
|
break;
|
|
|
|
case kTypeFloat:
|
|
if (oitem == NULL || item.u.floatValue != oitem->u.floatValue) {
|
|
diff->setFloat(item.mName, item.u.sizeValue);
|
|
}
|
|
break;
|
|
|
|
case kTypeDouble:
|
|
if (oitem == NULL || item.u.doubleValue != oitem->u.doubleValue) {
|
|
diff->setDouble(item.mName, item.u.sizeValue);
|
|
}
|
|
break;
|
|
|
|
case kTypeString:
|
|
if (oitem == NULL || *item.u.stringValue != *oitem->u.stringValue) {
|
|
diff->setString(item.mName, *item.u.stringValue);
|
|
}
|
|
break;
|
|
|
|
case kTypeRect:
|
|
if (oitem == NULL || memcmp(&item.u.rectValue, &oitem->u.rectValue, sizeof(Rect))) {
|
|
diff->setRect(
|
|
item.mName, item.u.rectValue.mLeft, item.u.rectValue.mTop,
|
|
item.u.rectValue.mRight, item.u.rectValue.mBottom);
|
|
}
|
|
break;
|
|
|
|
case kTypePointer:
|
|
if (oitem == NULL || item.u.ptrValue != oitem->u.ptrValue) {
|
|
diff->setPointer(item.mName, item.u.ptrValue);
|
|
}
|
|
break;
|
|
|
|
case kTypeBuffer:
|
|
{
|
|
sp<ABuffer> myBuf = static_cast<ABuffer *>(item.u.refValue);
|
|
if (myBuf == NULL) {
|
|
if (oitem == NULL || oitem->u.refValue != NULL) {
|
|
diff->setBuffer(item.mName, NULL);
|
|
}
|
|
break;
|
|
}
|
|
sp<ABuffer> oBuf = oitem == NULL ? NULL : static_cast<ABuffer *>(oitem->u.refValue);
|
|
if (oBuf == NULL
|
|
|| myBuf->size() != oBuf->size()
|
|
|| (!myBuf->data() ^ !oBuf->data()) // data nullness differs
|
|
|| (myBuf->data() && memcmp(myBuf->data(), oBuf->data(), myBuf->size()))) {
|
|
diff->setBuffer(item.mName, myBuf);
|
|
}
|
|
break;
|
|
}
|
|
|
|
case kTypeMessage:
|
|
{
|
|
sp<AMessage> myMsg = static_cast<AMessage *>(item.u.refValue);
|
|
if (myMsg == NULL) {
|
|
if (oitem == NULL || oitem->u.refValue != NULL) {
|
|
diff->setMessage(item.mName, NULL);
|
|
}
|
|
break;
|
|
}
|
|
sp<AMessage> oMsg =
|
|
oitem == NULL ? NULL : static_cast<AMessage *>(oitem->u.refValue);
|
|
sp<AMessage> changes = myMsg->changesFrom(oMsg, deep);
|
|
if (changes->countEntries()) {
|
|
diff->setMessage(item.mName, deep ? changes : myMsg);
|
|
}
|
|
break;
|
|
}
|
|
|
|
case kTypeObject:
|
|
if (oitem == NULL || item.u.refValue != oitem->u.refValue) {
|
|
diff->setObject(item.mName, item.u.refValue);
|
|
}
|
|
break;
|
|
|
|
default:
|
|
{
|
|
ALOGE("Unknown type %d", item.mType);
|
|
TRESPASS();
|
|
}
|
|
}
|
|
}
|
|
return diff;
|
|
}
|
|
|
|
size_t AMessage::countEntries() const {
|
|
return mItems.size();
|
|
}
|
|
|
|
/* static */
|
|
size_t AMessage::maxAllowedEntries() {
|
|
return kMaxNumItems;
|
|
}
|
|
|
|
const char *AMessage::getEntryNameAt(size_t index, Type *type) const {
|
|
if (index >= mItems.size()) {
|
|
*type = kTypeInt32;
|
|
|
|
return NULL;
|
|
}
|
|
|
|
*type = mItems[index].mType;
|
|
|
|
return mItems[index].mName;
|
|
}
|
|
|
|
AMessage::ItemData AMessage::getEntryAt(size_t index) const {
|
|
ItemData it;
|
|
if (index < mItems.size()) {
|
|
switch (mItems[index].mType) {
|
|
case kTypeInt32: it.set(mItems[index].u.int32Value); break;
|
|
case kTypeInt64: it.set(mItems[index].u.int64Value); break;
|
|
case kTypeSize: it.set(mItems[index].u.sizeValue); break;
|
|
case kTypeFloat: it.set(mItems[index].u.floatValue); break;
|
|
case kTypeDouble: it.set(mItems[index].u.doubleValue); break;
|
|
case kTypePointer: it.set(mItems[index].u.ptrValue); break;
|
|
case kTypeRect: it.set(mItems[index].u.rectValue); break;
|
|
case kTypeString: it.set(*mItems[index].u.stringValue); break;
|
|
case kTypeObject: {
|
|
sp<RefBase> obj = mItems[index].u.refValue;
|
|
it.set(obj);
|
|
break;
|
|
}
|
|
case kTypeMessage: {
|
|
sp<AMessage> msg = static_cast<AMessage *>(mItems[index].u.refValue);
|
|
it.set(msg);
|
|
break;
|
|
}
|
|
case kTypeBuffer: {
|
|
sp<ABuffer> buf = static_cast<ABuffer *>(mItems[index].u.refValue);
|
|
it.set(buf);
|
|
break;
|
|
}
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
return it;
|
|
}
|
|
|
|
status_t AMessage::setEntryNameAt(size_t index, const char *name) {
|
|
if (index >= mItems.size()) {
|
|
return BAD_INDEX;
|
|
}
|
|
if (name == nullptr) {
|
|
return BAD_VALUE;
|
|
}
|
|
if (!strcmp(name, mItems[index].mName)) {
|
|
return OK; // name has not changed
|
|
}
|
|
size_t len = strlen(name);
|
|
if (findItemIndex(name, len) < mItems.size()) {
|
|
return ALREADY_EXISTS;
|
|
}
|
|
delete[] mItems[index].mName;
|
|
mItems[index].mName = nullptr;
|
|
mItems[index].setName(name, len);
|
|
return OK;
|
|
}
|
|
|
|
status_t AMessage::setEntryAt(size_t index, const ItemData &item) {
|
|
AString stringValue;
|
|
sp<RefBase> refValue;
|
|
sp<AMessage> msgValue;
|
|
sp<ABuffer> bufValue;
|
|
|
|
if (index >= mItems.size()) {
|
|
return BAD_INDEX;
|
|
}
|
|
if (!item.used()) {
|
|
return BAD_VALUE;
|
|
}
|
|
Item *dst = &mItems[index];
|
|
freeItemValue(dst);
|
|
|
|
// some values can be directly set with the getter. others need items to be allocated
|
|
if (item.find(&dst->u.int32Value)) {
|
|
dst->mType = kTypeInt32;
|
|
} else if (item.find(&dst->u.int64Value)) {
|
|
dst->mType = kTypeInt64;
|
|
} else if (item.find(&dst->u.sizeValue)) {
|
|
dst->mType = kTypeSize;
|
|
} else if (item.find(&dst->u.floatValue)) {
|
|
dst->mType = kTypeFloat;
|
|
} else if (item.find(&dst->u.doubleValue)) {
|
|
dst->mType = kTypeDouble;
|
|
} else if (item.find(&dst->u.ptrValue)) {
|
|
dst->mType = kTypePointer;
|
|
} else if (item.find(&dst->u.rectValue)) {
|
|
dst->mType = kTypeRect;
|
|
} else if (item.find(&stringValue)) {
|
|
dst->u.stringValue = new AString(stringValue);
|
|
dst->mType = kTypeString;
|
|
} else if (item.find(&refValue)) {
|
|
if (refValue != NULL) { refValue->incStrong(this); }
|
|
dst->u.refValue = refValue.get();
|
|
dst->mType = kTypeObject;
|
|
} else if (item.find(&msgValue)) {
|
|
if (msgValue != NULL) { msgValue->incStrong(this); }
|
|
dst->u.refValue = msgValue.get();
|
|
dst->mType = kTypeMessage;
|
|
} else if (item.find(&bufValue)) {
|
|
if (bufValue != NULL) { bufValue->incStrong(this); }
|
|
dst->u.refValue = bufValue.get();
|
|
dst->mType = kTypeBuffer;
|
|
} else {
|
|
// unsupported item - we should not be here.
|
|
dst->mType = kTypeInt32;
|
|
dst->u.int32Value = 0xDEADDEAD;
|
|
return BAD_TYPE;
|
|
}
|
|
return OK;
|
|
}
|
|
|
|
status_t AMessage::removeEntryAt(size_t index) {
|
|
if (index >= mItems.size()) {
|
|
return BAD_INDEX;
|
|
}
|
|
// delete entry data and objects
|
|
delete[] mItems[index].mName;
|
|
mItems[index].mName = nullptr;
|
|
freeItemValue(&mItems[index]);
|
|
|
|
// swap entry with last entry and clear last entry's data
|
|
size_t lastIndex = mItems.size() - 1;
|
|
if (index < lastIndex) {
|
|
mItems[index] = mItems[lastIndex];
|
|
mItems[lastIndex].mName = nullptr;
|
|
mItems[lastIndex].mType = kTypeInt32;
|
|
}
|
|
mItems.pop_back();
|
|
return OK;
|
|
}
|
|
|
|
status_t AMessage::removeEntryByName(const char *name) {
|
|
if (name == nullptr) {
|
|
return BAD_VALUE;
|
|
}
|
|
size_t index = findEntryByName(name);
|
|
if (index >= mItems.size()) {
|
|
return BAD_INDEX;
|
|
}
|
|
return removeEntryAt(index);
|
|
}
|
|
|
|
void AMessage::setItem(const char *name, const ItemData &item) {
|
|
if (item.used()) {
|
|
Item *it = allocateItem(name);
|
|
if (it != nullptr) {
|
|
setEntryAt(it - &mItems[0], item);
|
|
}
|
|
}
|
|
}
|
|
|
|
AMessage::ItemData AMessage::findItem(const char *name) const {
|
|
return getEntryAt(findEntryByName(name));
|
|
}
|
|
|
|
void AMessage::extend(const sp<AMessage> &other) {
|
|
// ignore null messages
|
|
if (other == nullptr) {
|
|
return;
|
|
}
|
|
|
|
for (size_t ix = 0; ix < other->mItems.size(); ++ix) {
|
|
Item *it = allocateItem(other->mItems[ix].mName);
|
|
if (it != nullptr) {
|
|
ItemData data = other->getEntryAt(ix);
|
|
setEntryAt(it - &mItems[0], data);
|
|
}
|
|
}
|
|
}
|
|
|
|
size_t AMessage::findEntryByName(const char *name) const {
|
|
return name == nullptr ? countEntries() : findItemIndex(name, strlen(name));
|
|
}
|
|
|
|
} // namespace android
|