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