Home | History | Annotate | Download | only in androidfw
      1 /*
      2  * Copyright (C) 2006-2007 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 #undef LOG_TAG
     18 #define LOG_TAG "CursorWindow"
     19 
     20 #include <androidfw/CursorWindow.h>
     21 #include <binder/Parcel.h>
     22 #include <utils/Log.h>
     23 
     24 #include <cutils/ashmem.h>
     25 #include <sys/mman.h>
     26 
     27 #include <assert.h>
     28 #include <string.h>
     29 #include <stdlib.h>
     30 
     31 namespace android {
     32 
     33 CursorWindow::CursorWindow(const String8& name, int ashmemFd,
     34         void* data, size_t size, bool readOnly) :
     35         mName(name), mAshmemFd(ashmemFd), mData(data), mSize(size), mReadOnly(readOnly) {
     36     mHeader = static_cast<Header*>(mData);
     37 }
     38 
     39 CursorWindow::~CursorWindow() {
     40     ::munmap(mData, mSize);
     41     ::close(mAshmemFd);
     42 }
     43 
     44 status_t CursorWindow::create(const String8& name, size_t size, CursorWindow** outCursorWindow) {
     45     String8 ashmemName("CursorWindow: ");
     46     ashmemName.append(name);
     47 
     48     status_t result;
     49     int ashmemFd = ashmem_create_region(ashmemName.string(), size);
     50     if (ashmemFd < 0) {
     51         result = -errno;
     52     } else {
     53         result = ashmem_set_prot_region(ashmemFd, PROT_READ | PROT_WRITE);
     54         if (result >= 0) {
     55             void* data = ::mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, ashmemFd, 0);
     56             if (data == MAP_FAILED) {
     57                 result = -errno;
     58             } else {
     59                 result = ashmem_set_prot_region(ashmemFd, PROT_READ);
     60                 if (result >= 0) {
     61                     CursorWindow* window = new CursorWindow(name, ashmemFd,
     62                             data, size, false /*readOnly*/);
     63                     result = window->clear();
     64                     if (!result) {
     65                         LOG_WINDOW("Created new CursorWindow: freeOffset=%d, "
     66                                 "numRows=%d, numColumns=%d, mSize=%d, mData=%p",
     67                                 window->mHeader->freeOffset,
     68                                 window->mHeader->numRows,
     69                                 window->mHeader->numColumns,
     70                                 window->mSize, window->mData);
     71                         *outCursorWindow = window;
     72                         return OK;
     73                     }
     74                     delete window;
     75                 }
     76             }
     77             ::munmap(data, size);
     78         }
     79         ::close(ashmemFd);
     80     }
     81     *outCursorWindow = NULL;
     82     return result;
     83 }
     84 
     85 status_t CursorWindow::createFromParcel(Parcel* parcel, CursorWindow** outCursorWindow) {
     86     String8 name = parcel->readString8();
     87 
     88     status_t result;
     89     int ashmemFd = parcel->readFileDescriptor();
     90     if (ashmemFd == int(BAD_TYPE)) {
     91         result = BAD_TYPE;
     92     } else {
     93         ssize_t size = ashmem_get_size_region(ashmemFd);
     94         if (size < 0) {
     95             result = UNKNOWN_ERROR;
     96         } else {
     97             int dupAshmemFd = ::dup(ashmemFd);
     98             if (dupAshmemFd < 0) {
     99                 result = -errno;
    100             } else {
    101                 void* data = ::mmap(NULL, size, PROT_READ, MAP_SHARED, dupAshmemFd, 0);
    102                 if (data == MAP_FAILED) {
    103                     result = -errno;
    104                 } else {
    105                     CursorWindow* window = new CursorWindow(name, dupAshmemFd,
    106                             data, size, true /*readOnly*/);
    107                     LOG_WINDOW("Created CursorWindow from parcel: freeOffset=%d, "
    108                             "numRows=%d, numColumns=%d, mSize=%d, mData=%p",
    109                             window->mHeader->freeOffset,
    110                             window->mHeader->numRows,
    111                             window->mHeader->numColumns,
    112                             window->mSize, window->mData);
    113                     *outCursorWindow = window;
    114                     return OK;
    115                 }
    116                 ::close(dupAshmemFd);
    117             }
    118         }
    119     }
    120     *outCursorWindow = NULL;
    121     return result;
    122 }
    123 
    124 status_t CursorWindow::writeToParcel(Parcel* parcel) {
    125     status_t status = parcel->writeString8(mName);
    126     if (!status) {
    127         status = parcel->writeDupFileDescriptor(mAshmemFd);
    128     }
    129     return status;
    130 }
    131 
    132 status_t CursorWindow::clear() {
    133     if (mReadOnly) {
    134         return INVALID_OPERATION;
    135     }
    136 
    137     mHeader->freeOffset = sizeof(Header) + sizeof(RowSlotChunk);
    138     mHeader->firstChunkOffset = sizeof(Header);
    139     mHeader->numRows = 0;
    140     mHeader->numColumns = 0;
    141 
    142     RowSlotChunk* firstChunk = static_cast<RowSlotChunk*>(offsetToPtr(mHeader->firstChunkOffset));
    143     firstChunk->nextChunkOffset = 0;
    144     return OK;
    145 }
    146 
    147 status_t CursorWindow::setNumColumns(uint32_t numColumns) {
    148     if (mReadOnly) {
    149         return INVALID_OPERATION;
    150     }
    151 
    152     uint32_t cur = mHeader->numColumns;
    153     if ((cur > 0 || mHeader->numRows > 0) && cur != numColumns) {
    154         ALOGE("Trying to go from %d columns to %d", cur, numColumns);
    155         return INVALID_OPERATION;
    156     }
    157     mHeader->numColumns = numColumns;
    158     return OK;
    159 }
    160 
    161 status_t CursorWindow::allocRow() {
    162     if (mReadOnly) {
    163         return INVALID_OPERATION;
    164     }
    165 
    166     // Fill in the row slot
    167     RowSlot* rowSlot = allocRowSlot();
    168     if (rowSlot == NULL) {
    169         return NO_MEMORY;
    170     }
    171 
    172     // Allocate the slots for the field directory
    173     size_t fieldDirSize = mHeader->numColumns * sizeof(FieldSlot);
    174     uint32_t fieldDirOffset = alloc(fieldDirSize, true /*aligned*/);
    175     if (!fieldDirOffset) {
    176         mHeader->numRows--;
    177         LOG_WINDOW("The row failed, so back out the new row accounting "
    178                 "from allocRowSlot %d", mHeader->numRows);
    179         return NO_MEMORY;
    180     }
    181     FieldSlot* fieldDir = static_cast<FieldSlot*>(offsetToPtr(fieldDirOffset));
    182     memset(fieldDir, 0, fieldDirSize);
    183 
    184     LOG_WINDOW("Allocated row %u, rowSlot is at offset %u, fieldDir is %d bytes at offset %u\n",
    185             mHeader->numRows - 1, offsetFromPtr(rowSlot), fieldDirSize, fieldDirOffset);
    186     rowSlot->offset = fieldDirOffset;
    187     return OK;
    188 }
    189 
    190 status_t CursorWindow::freeLastRow() {
    191     if (mReadOnly) {
    192         return INVALID_OPERATION;
    193     }
    194 
    195     if (mHeader->numRows > 0) {
    196         mHeader->numRows--;
    197     }
    198     return OK;
    199 }
    200 
    201 uint32_t CursorWindow::alloc(size_t size, bool aligned) {
    202     uint32_t padding;
    203     if (aligned) {
    204         // 4 byte alignment
    205         padding = (~mHeader->freeOffset + 1) & 3;
    206     } else {
    207         padding = 0;
    208     }
    209 
    210     uint32_t offset = mHeader->freeOffset + padding;
    211     uint32_t nextFreeOffset = offset + size;
    212     if (nextFreeOffset > mSize) {
    213         ALOGW("Window is full: requested allocation %zu bytes, "
    214                 "free space %zu bytes, window size %zu bytes",
    215                 size, freeSpace(), mSize);
    216         return 0;
    217     }
    218 
    219     mHeader->freeOffset = nextFreeOffset;
    220     return offset;
    221 }
    222 
    223 CursorWindow::RowSlot* CursorWindow::getRowSlot(uint32_t row) {
    224     uint32_t chunkPos = row;
    225     RowSlotChunk* chunk = static_cast<RowSlotChunk*>(
    226             offsetToPtr(mHeader->firstChunkOffset));
    227     while (chunkPos >= ROW_SLOT_CHUNK_NUM_ROWS) {
    228         chunk = static_cast<RowSlotChunk*>(offsetToPtr(chunk->nextChunkOffset));
    229         chunkPos -= ROW_SLOT_CHUNK_NUM_ROWS;
    230     }
    231     return &chunk->slots[chunkPos];
    232 }
    233 
    234 CursorWindow::RowSlot* CursorWindow::allocRowSlot() {
    235     uint32_t chunkPos = mHeader->numRows;
    236     RowSlotChunk* chunk = static_cast<RowSlotChunk*>(
    237             offsetToPtr(mHeader->firstChunkOffset));
    238     while (chunkPos > ROW_SLOT_CHUNK_NUM_ROWS) {
    239         chunk = static_cast<RowSlotChunk*>(offsetToPtr(chunk->nextChunkOffset));
    240         chunkPos -= ROW_SLOT_CHUNK_NUM_ROWS;
    241     }
    242     if (chunkPos == ROW_SLOT_CHUNK_NUM_ROWS) {
    243         if (!chunk->nextChunkOffset) {
    244             chunk->nextChunkOffset = alloc(sizeof(RowSlotChunk), true /*aligned*/);
    245             if (!chunk->nextChunkOffset) {
    246                 return NULL;
    247             }
    248         }
    249         chunk = static_cast<RowSlotChunk*>(offsetToPtr(chunk->nextChunkOffset));
    250         chunk->nextChunkOffset = 0;
    251         chunkPos = 0;
    252     }
    253     mHeader->numRows += 1;
    254     return &chunk->slots[chunkPos];
    255 }
    256 
    257 CursorWindow::FieldSlot* CursorWindow::getFieldSlot(uint32_t row, uint32_t column) {
    258     if (row >= mHeader->numRows || column >= mHeader->numColumns) {
    259         ALOGE("Failed to read row %d, column %d from a CursorWindow which "
    260                 "has %d rows, %d columns.",
    261                 row, column, mHeader->numRows, mHeader->numColumns);
    262         return NULL;
    263     }
    264     RowSlot* rowSlot = getRowSlot(row);
    265     if (!rowSlot) {
    266         ALOGE("Failed to find rowSlot for row %d.", row);
    267         return NULL;
    268     }
    269     FieldSlot* fieldDir = static_cast<FieldSlot*>(offsetToPtr(rowSlot->offset));
    270     return &fieldDir[column];
    271 }
    272 
    273 status_t CursorWindow::putBlob(uint32_t row, uint32_t column, const void* value, size_t size) {
    274     return putBlobOrString(row, column, value, size, FIELD_TYPE_BLOB);
    275 }
    276 
    277 status_t CursorWindow::putString(uint32_t row, uint32_t column, const char* value,
    278         size_t sizeIncludingNull) {
    279     return putBlobOrString(row, column, value, sizeIncludingNull, FIELD_TYPE_STRING);
    280 }
    281 
    282 status_t CursorWindow::putBlobOrString(uint32_t row, uint32_t column,
    283         const void* value, size_t size, int32_t type) {
    284     if (mReadOnly) {
    285         return INVALID_OPERATION;
    286     }
    287 
    288     FieldSlot* fieldSlot = getFieldSlot(row, column);
    289     if (!fieldSlot) {
    290         return BAD_VALUE;
    291     }
    292 
    293     uint32_t offset = alloc(size);
    294     if (!offset) {
    295         return NO_MEMORY;
    296     }
    297 
    298     memcpy(offsetToPtr(offset), value, size);
    299 
    300     fieldSlot->type = type;
    301     fieldSlot->data.buffer.offset = offset;
    302     fieldSlot->data.buffer.size = size;
    303     return OK;
    304 }
    305 
    306 status_t CursorWindow::putLong(uint32_t row, uint32_t column, int64_t value) {
    307     if (mReadOnly) {
    308         return INVALID_OPERATION;
    309     }
    310 
    311     FieldSlot* fieldSlot = getFieldSlot(row, column);
    312     if (!fieldSlot) {
    313         return BAD_VALUE;
    314     }
    315 
    316     fieldSlot->type = FIELD_TYPE_INTEGER;
    317     fieldSlot->data.l = value;
    318     return OK;
    319 }
    320 
    321 status_t CursorWindow::putDouble(uint32_t row, uint32_t column, double value) {
    322     if (mReadOnly) {
    323         return INVALID_OPERATION;
    324     }
    325 
    326     FieldSlot* fieldSlot = getFieldSlot(row, column);
    327     if (!fieldSlot) {
    328         return BAD_VALUE;
    329     }
    330 
    331     fieldSlot->type = FIELD_TYPE_FLOAT;
    332     fieldSlot->data.d = value;
    333     return OK;
    334 }
    335 
    336 status_t CursorWindow::putNull(uint32_t row, uint32_t column) {
    337     if (mReadOnly) {
    338         return INVALID_OPERATION;
    339     }
    340 
    341     FieldSlot* fieldSlot = getFieldSlot(row, column);
    342     if (!fieldSlot) {
    343         return BAD_VALUE;
    344     }
    345 
    346     fieldSlot->type = FIELD_TYPE_NULL;
    347     fieldSlot->data.buffer.offset = 0;
    348     fieldSlot->data.buffer.size = 0;
    349     return OK;
    350 }
    351 
    352 }; // namespace android
    353