9fd251e7cb
This change removes an unneeded debug log in CursorWindow that uses 0.37% of gms.persistent CPU. Test: Build and flash Bug: 184541591 Change-Id: I95683af27904db8d5ec61761dfda6d6d53ddbbef
437 lines
12 KiB
C++
437 lines
12 KiB
C++
/*
|
|
* Copyright (C) 2006-2007 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 "CursorWindow"
|
|
|
|
#include <androidfw/CursorWindow.h>
|
|
|
|
#include <sys/mman.h>
|
|
|
|
#include "android-base/logging.h"
|
|
#include "cutils/ashmem.h"
|
|
|
|
namespace android {
|
|
|
|
/**
|
|
* By default windows are lightweight inline allocations of this size;
|
|
* they're only inflated to ashmem regions when more space is needed.
|
|
*/
|
|
static constexpr const size_t kInlineSize = 16384;
|
|
|
|
static constexpr const size_t kSlotShift = 4;
|
|
static constexpr const size_t kSlotSizeBytes = 1 << kSlotShift;
|
|
|
|
CursorWindow::CursorWindow() {
|
|
}
|
|
|
|
CursorWindow::~CursorWindow() {
|
|
if (mAshmemFd != -1) {
|
|
::munmap(mData, mSize);
|
|
::close(mAshmemFd);
|
|
} else {
|
|
free(mData);
|
|
}
|
|
}
|
|
|
|
status_t CursorWindow::create(const String8 &name, size_t inflatedSize, CursorWindow **outWindow) {
|
|
*outWindow = nullptr;
|
|
|
|
CursorWindow* window = new CursorWindow();
|
|
if (!window) goto fail;
|
|
|
|
window->mName = name;
|
|
window->mSize = std::min(kInlineSize, inflatedSize);
|
|
window->mInflatedSize = inflatedSize;
|
|
window->mData = malloc(window->mSize);
|
|
if (!window->mData) goto fail;
|
|
window->mReadOnly = false;
|
|
|
|
window->clear();
|
|
window->updateSlotsData();
|
|
|
|
*outWindow = window;
|
|
return OK;
|
|
|
|
fail:
|
|
LOG(ERROR) << "Failed create";
|
|
fail_silent:
|
|
delete window;
|
|
return UNKNOWN_ERROR;
|
|
}
|
|
|
|
status_t CursorWindow::maybeInflate() {
|
|
int ashmemFd = 0;
|
|
void* newData = nullptr;
|
|
|
|
// Bail early when we can't expand any further
|
|
if (mReadOnly || mSize == mInflatedSize) {
|
|
return INVALID_OPERATION;
|
|
}
|
|
|
|
String8 ashmemName("CursorWindow: ");
|
|
ashmemName.append(mName);
|
|
|
|
ashmemFd = ashmem_create_region(ashmemName.string(), mInflatedSize);
|
|
if (ashmemFd < 0) {
|
|
PLOG(ERROR) << "Failed ashmem_create_region";
|
|
goto fail_silent;
|
|
}
|
|
|
|
if (ashmem_set_prot_region(ashmemFd, PROT_READ | PROT_WRITE) < 0) {
|
|
PLOG(ERROR) << "Failed ashmem_set_prot_region";
|
|
goto fail_silent;
|
|
}
|
|
|
|
newData = ::mmap(nullptr, mInflatedSize, PROT_READ | PROT_WRITE, MAP_SHARED, ashmemFd, 0);
|
|
if (newData == MAP_FAILED) {
|
|
PLOG(ERROR) << "Failed mmap";
|
|
goto fail_silent;
|
|
}
|
|
|
|
if (ashmem_set_prot_region(ashmemFd, PROT_READ) < 0) {
|
|
PLOG(ERROR) << "Failed ashmem_set_prot_region";
|
|
goto fail_silent;
|
|
}
|
|
|
|
{
|
|
// Migrate existing contents into new ashmem region
|
|
uint32_t slotsSize = mSize - mSlotsOffset;
|
|
uint32_t newSlotsOffset = mInflatedSize - slotsSize;
|
|
memcpy(static_cast<uint8_t*>(newData),
|
|
static_cast<uint8_t*>(mData), mAllocOffset);
|
|
memcpy(static_cast<uint8_t*>(newData) + newSlotsOffset,
|
|
static_cast<uint8_t*>(mData) + mSlotsOffset, slotsSize);
|
|
|
|
free(mData);
|
|
mAshmemFd = ashmemFd;
|
|
mData = newData;
|
|
mSize = mInflatedSize;
|
|
mSlotsOffset = newSlotsOffset;
|
|
|
|
updateSlotsData();
|
|
}
|
|
|
|
LOG(DEBUG) << "Inflated: " << this->toString();
|
|
return OK;
|
|
|
|
fail:
|
|
LOG(ERROR) << "Failed maybeInflate";
|
|
fail_silent:
|
|
::munmap(newData, mInflatedSize);
|
|
::close(ashmemFd);
|
|
return UNKNOWN_ERROR;
|
|
}
|
|
|
|
status_t CursorWindow::createFromParcel(Parcel* parcel, CursorWindow** outWindow) {
|
|
*outWindow = nullptr;
|
|
|
|
CursorWindow* window = new CursorWindow();
|
|
if (!window) goto fail;
|
|
|
|
if (parcel->readString8(&window->mName)) goto fail;
|
|
if (parcel->readUint32(&window->mNumRows)) goto fail;
|
|
if (parcel->readUint32(&window->mNumColumns)) goto fail;
|
|
if (parcel->readUint32(&window->mSize)) goto fail;
|
|
|
|
if ((window->mNumRows * window->mNumColumns * kSlotSizeBytes) > window->mSize) {
|
|
LOG(ERROR) << "Unexpected size " << window->mSize << " for " << window->mNumRows
|
|
<< " rows and " << window->mNumColumns << " columns";
|
|
goto fail_silent;
|
|
}
|
|
|
|
bool isAshmem;
|
|
if (parcel->readBool(&isAshmem)) goto fail;
|
|
if (isAshmem) {
|
|
window->mAshmemFd = parcel->readFileDescriptor();
|
|
if (window->mAshmemFd < 0) {
|
|
LOG(ERROR) << "Failed readFileDescriptor";
|
|
goto fail_silent;
|
|
}
|
|
|
|
window->mAshmemFd = ::fcntl(window->mAshmemFd, F_DUPFD_CLOEXEC, 0);
|
|
if (window->mAshmemFd < 0) {
|
|
PLOG(ERROR) << "Failed F_DUPFD_CLOEXEC";
|
|
goto fail_silent;
|
|
}
|
|
|
|
window->mData = ::mmap(nullptr, window->mSize, PROT_READ, MAP_SHARED, window->mAshmemFd, 0);
|
|
if (window->mData == MAP_FAILED) {
|
|
PLOG(ERROR) << "Failed mmap";
|
|
goto fail_silent;
|
|
}
|
|
} else {
|
|
window->mAshmemFd = -1;
|
|
|
|
if (window->mSize > kInlineSize) {
|
|
LOG(ERROR) << "Unexpected size " << window->mSize << " for inline window";
|
|
goto fail_silent;
|
|
}
|
|
|
|
window->mData = malloc(window->mSize);
|
|
if (!window->mData) goto fail;
|
|
|
|
if (parcel->read(window->mData, window->mSize)) goto fail;
|
|
}
|
|
|
|
// We just came from a remote source, so we're read-only
|
|
// and we can't inflate ourselves
|
|
window->mInflatedSize = window->mSize;
|
|
window->mReadOnly = true;
|
|
|
|
window->updateSlotsData();
|
|
|
|
LOG(DEBUG) << "Created from parcel: " << window->toString();
|
|
*outWindow = window;
|
|
return OK;
|
|
|
|
fail:
|
|
LOG(ERROR) << "Failed createFromParcel";
|
|
fail_silent:
|
|
delete window;
|
|
return UNKNOWN_ERROR;
|
|
}
|
|
|
|
status_t CursorWindow::writeToParcel(Parcel* parcel) {
|
|
LOG(DEBUG) << "Writing to parcel: " << this->toString();
|
|
|
|
if (parcel->writeString8(mName)) goto fail;
|
|
if (parcel->writeUint32(mNumRows)) goto fail;
|
|
if (parcel->writeUint32(mNumColumns)) goto fail;
|
|
if (mAshmemFd != -1) {
|
|
if (parcel->writeUint32(mSize)) goto fail;
|
|
if (parcel->writeBool(true)) goto fail;
|
|
if (parcel->writeDupFileDescriptor(mAshmemFd)) goto fail;
|
|
} else {
|
|
// Since we know we're going to be read-only on the remote side,
|
|
// we can compact ourselves on the wire, with just enough padding
|
|
// to ensure our slots stay aligned
|
|
size_t slotsSize = mSize - mSlotsOffset;
|
|
size_t compactedSize = mAllocOffset + slotsSize;
|
|
compactedSize = (compactedSize + 3) & ~3;
|
|
if (parcel->writeUint32(compactedSize)) goto fail;
|
|
if (parcel->writeBool(false)) goto fail;
|
|
void* dest = parcel->writeInplace(compactedSize);
|
|
if (!dest) goto fail;
|
|
memcpy(static_cast<uint8_t*>(dest),
|
|
static_cast<uint8_t*>(mData), mAllocOffset);
|
|
memcpy(static_cast<uint8_t*>(dest) + compactedSize - slotsSize,
|
|
static_cast<uint8_t*>(mData) + mSlotsOffset, slotsSize);
|
|
}
|
|
return OK;
|
|
|
|
fail:
|
|
LOG(ERROR) << "Failed writeToParcel";
|
|
fail_silent:
|
|
return UNKNOWN_ERROR;
|
|
}
|
|
|
|
status_t CursorWindow::clear() {
|
|
if (mReadOnly) {
|
|
return INVALID_OPERATION;
|
|
}
|
|
mAllocOffset = 0;
|
|
mSlotsOffset = mSize;
|
|
mNumRows = 0;
|
|
mNumColumns = 0;
|
|
return OK;
|
|
}
|
|
|
|
void CursorWindow::updateSlotsData() {
|
|
mSlotsStart = static_cast<uint8_t*>(mData) + mSize - kSlotSizeBytes;
|
|
mSlotsEnd = static_cast<uint8_t*>(mData) + mSlotsOffset;
|
|
}
|
|
|
|
void* CursorWindow::offsetToPtr(uint32_t offset, uint32_t bufferSize = 0) {
|
|
if (offset > mSize) {
|
|
LOG(ERROR) << "Offset " << offset
|
|
<< " out of bounds, max value " << mSize;
|
|
return nullptr;
|
|
}
|
|
if (offset + bufferSize > mSize) {
|
|
LOG(ERROR) << "End offset " << (offset + bufferSize)
|
|
<< " out of bounds, max value " << mSize;
|
|
return nullptr;
|
|
}
|
|
return static_cast<uint8_t*>(mData) + offset;
|
|
}
|
|
|
|
uint32_t CursorWindow::offsetFromPtr(void* ptr) {
|
|
return static_cast<uint8_t*>(ptr) - static_cast<uint8_t*>(mData);
|
|
}
|
|
|
|
status_t CursorWindow::setNumColumns(uint32_t numColumns) {
|
|
if (mReadOnly) {
|
|
return INVALID_OPERATION;
|
|
}
|
|
uint32_t cur = mNumColumns;
|
|
if ((cur > 0 || mNumRows > 0) && cur != numColumns) {
|
|
LOG(ERROR) << "Trying to go from " << cur << " columns to " << numColumns;
|
|
return INVALID_OPERATION;
|
|
}
|
|
mNumColumns = numColumns;
|
|
return OK;
|
|
}
|
|
|
|
status_t CursorWindow::allocRow() {
|
|
if (mReadOnly) {
|
|
return INVALID_OPERATION;
|
|
}
|
|
size_t size = mNumColumns * kSlotSizeBytes;
|
|
int32_t newOffset = mSlotsOffset - size;
|
|
if (newOffset < (int32_t) mAllocOffset) {
|
|
maybeInflate();
|
|
newOffset = mSlotsOffset - size;
|
|
if (newOffset < (int32_t) mAllocOffset) {
|
|
return NO_MEMORY;
|
|
}
|
|
}
|
|
memset(offsetToPtr(newOffset), 0, size);
|
|
mSlotsOffset = newOffset;
|
|
updateSlotsData();
|
|
mNumRows++;
|
|
return OK;
|
|
}
|
|
|
|
status_t CursorWindow::freeLastRow() {
|
|
if (mReadOnly) {
|
|
return INVALID_OPERATION;
|
|
}
|
|
size_t size = mNumColumns * kSlotSizeBytes;
|
|
size_t newOffset = mSlotsOffset + size;
|
|
if (newOffset > mSize) {
|
|
return NO_MEMORY;
|
|
}
|
|
mSlotsOffset = newOffset;
|
|
updateSlotsData();
|
|
mNumRows--;
|
|
return OK;
|
|
}
|
|
|
|
status_t CursorWindow::alloc(size_t size, uint32_t* outOffset) {
|
|
if (mReadOnly) {
|
|
return INVALID_OPERATION;
|
|
}
|
|
size_t alignedSize = (size + 3) & ~3;
|
|
size_t newOffset = mAllocOffset + alignedSize;
|
|
if (newOffset > mSlotsOffset) {
|
|
maybeInflate();
|
|
newOffset = mAllocOffset + alignedSize;
|
|
if (newOffset > mSlotsOffset) {
|
|
return NO_MEMORY;
|
|
}
|
|
}
|
|
*outOffset = mAllocOffset;
|
|
mAllocOffset = newOffset;
|
|
return OK;
|
|
}
|
|
|
|
CursorWindow::FieldSlot* CursorWindow::getFieldSlot(uint32_t row, uint32_t column) {
|
|
// This is carefully tuned to use as few cycles as
|
|
// possible, since this is an extremely hot code path;
|
|
// see CursorWindow_bench.cpp for more details
|
|
void *result = static_cast<uint8_t*>(mSlotsStart)
|
|
- (((row * mNumColumns) + column) << kSlotShift);
|
|
if (result < mSlotsEnd || result > mSlotsStart || column >= mNumColumns) {
|
|
LOG(ERROR) << "Failed to read row " << row << ", column " << column
|
|
<< " from a window with " << mNumRows << " rows, " << mNumColumns << " columns";
|
|
return nullptr;
|
|
} else {
|
|
return static_cast<FieldSlot*>(result);
|
|
}
|
|
}
|
|
|
|
status_t CursorWindow::putBlob(uint32_t row, uint32_t column, const void* value, size_t size) {
|
|
return putBlobOrString(row, column, value, size, FIELD_TYPE_BLOB);
|
|
}
|
|
|
|
status_t CursorWindow::putString(uint32_t row, uint32_t column, const char* value,
|
|
size_t sizeIncludingNull) {
|
|
return putBlobOrString(row, column, value, sizeIncludingNull, FIELD_TYPE_STRING);
|
|
}
|
|
|
|
status_t CursorWindow::putBlobOrString(uint32_t row, uint32_t column,
|
|
const void* value, size_t size, int32_t type) {
|
|
if (mReadOnly) {
|
|
return INVALID_OPERATION;
|
|
}
|
|
|
|
FieldSlot* fieldSlot = getFieldSlot(row, column);
|
|
if (!fieldSlot) {
|
|
return BAD_VALUE;
|
|
}
|
|
|
|
uint32_t offset;
|
|
if (alloc(size, &offset)) {
|
|
return NO_MEMORY;
|
|
}
|
|
|
|
memcpy(offsetToPtr(offset), value, size);
|
|
|
|
fieldSlot = getFieldSlot(row, column);
|
|
fieldSlot->type = type;
|
|
fieldSlot->data.buffer.offset = offset;
|
|
fieldSlot->data.buffer.size = size;
|
|
return OK;
|
|
}
|
|
|
|
status_t CursorWindow::putLong(uint32_t row, uint32_t column, int64_t value) {
|
|
if (mReadOnly) {
|
|
return INVALID_OPERATION;
|
|
}
|
|
|
|
FieldSlot* fieldSlot = getFieldSlot(row, column);
|
|
if (!fieldSlot) {
|
|
return BAD_VALUE;
|
|
}
|
|
|
|
fieldSlot->type = FIELD_TYPE_INTEGER;
|
|
fieldSlot->data.l = value;
|
|
return OK;
|
|
}
|
|
|
|
status_t CursorWindow::putDouble(uint32_t row, uint32_t column, double value) {
|
|
if (mReadOnly) {
|
|
return INVALID_OPERATION;
|
|
}
|
|
|
|
FieldSlot* fieldSlot = getFieldSlot(row, column);
|
|
if (!fieldSlot) {
|
|
return BAD_VALUE;
|
|
}
|
|
|
|
fieldSlot->type = FIELD_TYPE_FLOAT;
|
|
fieldSlot->data.d = value;
|
|
return OK;
|
|
}
|
|
|
|
status_t CursorWindow::putNull(uint32_t row, uint32_t column) {
|
|
if (mReadOnly) {
|
|
return INVALID_OPERATION;
|
|
}
|
|
|
|
FieldSlot* fieldSlot = getFieldSlot(row, column);
|
|
if (!fieldSlot) {
|
|
return BAD_VALUE;
|
|
}
|
|
|
|
fieldSlot->type = FIELD_TYPE_NULL;
|
|
fieldSlot->data.buffer.offset = 0;
|
|
fieldSlot->data.buffer.size = 0;
|
|
return OK;
|
|
}
|
|
|
|
}; // namespace android
|