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 #define LOG_TAG "CursorWindow"
18
19 #include <androidfw/CursorWindow.h>
20
21 #include <sys/mman.h>
22
23 #include "android-base/logging.h"
24 #include "cutils/ashmem.h"
25
26 namespace android {
27
28 /**
29 * By default windows are lightweight inline allocations of this size;
30 * they're only inflated to ashmem regions when more space is needed.
31 */
32 static constexpr const size_t kInlineSize = 16384;
33
34 static constexpr const size_t kSlotShift = 4;
35 static constexpr const size_t kSlotSizeBytes = 1 << kSlotShift;
36
CursorWindow()37 CursorWindow::CursorWindow() {
38 }
39
~CursorWindow()40 CursorWindow::~CursorWindow() {
41 if (mAshmemFd >= 0) {
42 ::munmap(mData, mSize);
43 ::close(mAshmemFd);
44 } else {
45 free(mData);
46 }
47 }
48
create(const String8 & name,size_t inflatedSize,CursorWindow ** outWindow)49 status_t CursorWindow::create(const String8 &name, size_t inflatedSize, CursorWindow **outWindow) {
50 *outWindow = nullptr;
51
52 CursorWindow* window = new CursorWindow();
53 if (!window) goto fail;
54
55 window->mName = name;
56 window->mSize = std::min(kInlineSize, inflatedSize);
57 window->mInflatedSize = inflatedSize;
58 window->mData = malloc(window->mSize);
59 if (!window->mData) goto fail;
60 window->mReadOnly = false;
61
62 window->clear();
63 window->updateSlotsData();
64
65 *outWindow = window;
66 return OK;
67
68 fail:
69 LOG(ERROR) << "Failed create";
70 fail_silent:
71 delete window;
72 return UNKNOWN_ERROR;
73 }
74
maybeInflate()75 status_t CursorWindow::maybeInflate() {
76 int ashmemFd = 0;
77 void* newData = nullptr;
78
79 // Bail early when we can't expand any further
80 if (mReadOnly || mSize == mInflatedSize) {
81 return INVALID_OPERATION;
82 }
83
84 String8 ashmemName("CursorWindow: ");
85 ashmemName.append(mName);
86
87 ashmemFd = ashmem_create_region(ashmemName.c_str(), mInflatedSize);
88 if (ashmemFd < 0) {
89 PLOG(ERROR) << "Failed ashmem_create_region";
90 goto fail_silent;
91 }
92
93 if (ashmem_set_prot_region(ashmemFd, PROT_READ | PROT_WRITE) < 0) {
94 PLOG(ERROR) << "Failed ashmem_set_prot_region";
95 goto fail_silent;
96 }
97
98 newData = ::mmap(nullptr, mInflatedSize, PROT_READ | PROT_WRITE, MAP_SHARED, ashmemFd, 0);
99 if (newData == MAP_FAILED) {
100 PLOG(ERROR) << "Failed mmap";
101 goto fail_silent;
102 }
103
104 if (ashmem_set_prot_region(ashmemFd, PROT_READ) < 0) {
105 PLOG(ERROR) << "Failed ashmem_set_prot_region";
106 goto fail_silent;
107 }
108
109 {
110 // Migrate existing contents into new ashmem region
111 uint32_t slotsSize = sizeOfSlots();
112 uint32_t newSlotsOffset = mInflatedSize - slotsSize;
113 memcpy(static_cast<uint8_t*>(newData),
114 static_cast<uint8_t*>(mData), mAllocOffset);
115 memcpy(static_cast<uint8_t*>(newData) + newSlotsOffset,
116 static_cast<uint8_t*>(mData) + mSlotsOffset, slotsSize);
117
118 free(mData);
119 mAshmemFd = ashmemFd;
120 mData = newData;
121 mSize = mInflatedSize;
122 mSlotsOffset = newSlotsOffset;
123
124 updateSlotsData();
125 }
126
127 LOG(DEBUG) << "Inflated: " << this->toString();
128 return OK;
129
130 fail:
131 LOG(ERROR) << "Failed maybeInflate";
132 fail_silent:
133 ::munmap(newData, mInflatedSize);
134 ::close(ashmemFd);
135 return UNKNOWN_ERROR;
136 }
137
createFromParcel(Parcel * parcel,CursorWindow ** outWindow)138 status_t CursorWindow::createFromParcel(Parcel* parcel, CursorWindow** outWindow) {
139 *outWindow = nullptr;
140
141 CursorWindow* window = new CursorWindow();
142 if (!window) goto fail;
143
144 if (parcel->readString8(&window->mName)) goto fail;
145 if (parcel->readUint32(&window->mNumRows)) goto fail;
146 if (parcel->readUint32(&window->mNumColumns)) goto fail;
147 if (parcel->readUint32(&window->mSize)) goto fail;
148
149 if ((window->mNumRows * window->mNumColumns * kSlotSizeBytes) > window->mSize) {
150 LOG(ERROR) << "Unexpected size " << window->mSize << " for " << window->mNumRows
151 << " rows and " << window->mNumColumns << " columns";
152 goto fail_silent;
153 }
154
155 bool isAshmem;
156 if (parcel->readBool(&isAshmem)) goto fail;
157 if (isAshmem) {
158 int tempFd = parcel->readFileDescriptor();
159 if (tempFd < 0) {
160 LOG(ERROR) << "Failed readFileDescriptor";
161 goto fail_silent;
162 }
163
164 tempFd = ::fcntl(tempFd, F_DUPFD_CLOEXEC, 0);
165 if (tempFd < 0) {
166 PLOG(ERROR) << "Failed F_DUPFD_CLOEXEC";
167 goto fail_silent;
168 }
169
170 window->mData = ::mmap(nullptr, window->mSize, PROT_READ, MAP_SHARED, tempFd, 0);
171 if (window->mData == MAP_FAILED) {
172 ::close(tempFd);
173 PLOG(ERROR) << "Failed mmap";
174 goto fail_silent;
175 }
176
177 window->mAshmemFd = tempFd;
178
179 } else {
180 window->mAshmemFd = -1;
181
182 if (window->mSize > kInlineSize) {
183 LOG(ERROR) << "Unexpected size " << window->mSize << " for inline window";
184 goto fail_silent;
185 }
186
187 window->mData = malloc(window->mSize);
188 if (!window->mData) goto fail;
189
190 if (parcel->read(window->mData, window->mSize)) goto fail;
191 }
192
193 // We just came from a remote source, so we're read-only
194 // and we can't inflate ourselves
195 window->mInflatedSize = window->mSize;
196 window->mReadOnly = true;
197
198 window->updateSlotsData();
199
200 LOG(DEBUG) << "Created from parcel: " << window->toString();
201 *outWindow = window;
202 return OK;
203
204 fail:
205 LOG(ERROR) << "Failed createFromParcel";
206 fail_silent:
207 delete window;
208 return UNKNOWN_ERROR;
209 }
210
writeToParcel(Parcel * parcel)211 status_t CursorWindow::writeToParcel(Parcel* parcel) {
212 LOG(DEBUG) << "Writing to parcel: " << this->toString();
213
214 if (parcel->writeString8(mName)) goto fail;
215 if (parcel->writeUint32(mNumRows)) goto fail;
216 if (parcel->writeUint32(mNumColumns)) goto fail;
217 if (mAshmemFd != -1) {
218 if (parcel->writeUint32(mSize)) goto fail;
219 if (parcel->writeBool(true)) goto fail;
220 if (parcel->writeDupFileDescriptor(mAshmemFd)) goto fail;
221 } else {
222 // Since we know we're going to be read-only on the remote side,
223 // we can compact ourselves on the wire.
224 size_t slotsSize = sizeOfSlots();
225 size_t compactedSize = sizeInUse();
226 if (parcel->writeUint32(compactedSize)) goto fail;
227 if (parcel->writeBool(false)) goto fail;
228 void* dest = parcel->writeInplace(compactedSize);
229 if (!dest) goto fail;
230 memcpy(static_cast<uint8_t*>(dest),
231 static_cast<uint8_t*>(mData), mAllocOffset);
232 memcpy(static_cast<uint8_t*>(dest) + compactedSize - slotsSize,
233 static_cast<uint8_t*>(mData) + mSlotsOffset, slotsSize);
234 }
235 return OK;
236
237 fail:
238 LOG(ERROR) << "Failed writeToParcel";
239 fail_silent:
240 return UNKNOWN_ERROR;
241 }
242
clear()243 status_t CursorWindow::clear() {
244 if (mReadOnly) {
245 return INVALID_OPERATION;
246 }
247 mAllocOffset = 0;
248 mSlotsOffset = mSize;
249 mNumRows = 0;
250 mNumColumns = 0;
251 return OK;
252 }
253
updateSlotsData()254 void CursorWindow::updateSlotsData() {
255 mSlotsStart = static_cast<uint8_t*>(mData) + mSize - kSlotSizeBytes;
256 mSlotsEnd = static_cast<uint8_t*>(mData) + mSlotsOffset;
257 }
258
offsetToPtr(uint32_t offset,uint32_t bufferSize=0)259 void* CursorWindow::offsetToPtr(uint32_t offset, uint32_t bufferSize = 0) {
260 if (offset > mSize) {
261 LOG(ERROR) << "Offset " << offset
262 << " out of bounds, max value " << mSize;
263 return nullptr;
264 }
265 if (offset + bufferSize > mSize) {
266 LOG(ERROR) << "End offset " << (offset + bufferSize)
267 << " out of bounds, max value " << mSize;
268 return nullptr;
269 }
270 return static_cast<uint8_t*>(mData) + offset;
271 }
272
offsetFromPtr(void * ptr)273 uint32_t CursorWindow::offsetFromPtr(void* ptr) {
274 return static_cast<uint8_t*>(ptr) - static_cast<uint8_t*>(mData);
275 }
276
setNumColumns(uint32_t numColumns)277 status_t CursorWindow::setNumColumns(uint32_t numColumns) {
278 if (mReadOnly) {
279 return INVALID_OPERATION;
280 }
281 uint32_t cur = mNumColumns;
282 if ((cur > 0 || mNumRows > 0) && cur != numColumns) {
283 LOG(ERROR) << "Trying to go from " << cur << " columns to " << numColumns;
284 return INVALID_OPERATION;
285 }
286 mNumColumns = numColumns;
287 return OK;
288 }
289
allocRow()290 status_t CursorWindow::allocRow() {
291 if (mReadOnly) {
292 return INVALID_OPERATION;
293 }
294 size_t size = mNumColumns * kSlotSizeBytes;
295 int32_t newOffset = mSlotsOffset - size;
296 if (newOffset < (int32_t) mAllocOffset) {
297 maybeInflate();
298 newOffset = mSlotsOffset - size;
299 if (newOffset < (int32_t) mAllocOffset) {
300 return NO_MEMORY;
301 }
302 }
303 memset(offsetToPtr(newOffset), 0, size);
304 mSlotsOffset = newOffset;
305 updateSlotsData();
306 mNumRows++;
307 return OK;
308 }
309
freeLastRow()310 status_t CursorWindow::freeLastRow() {
311 if (mReadOnly) {
312 return INVALID_OPERATION;
313 }
314 size_t size = mNumColumns * kSlotSizeBytes;
315 size_t newOffset = mSlotsOffset + size;
316 if (newOffset > mSize) {
317 return NO_MEMORY;
318 }
319 mSlotsOffset = newOffset;
320 updateSlotsData();
321 mNumRows--;
322 return OK;
323 }
324
alloc(size_t size,uint32_t * outOffset)325 status_t CursorWindow::alloc(size_t size, uint32_t* outOffset) {
326 if (mReadOnly) {
327 return INVALID_OPERATION;
328 }
329 size_t alignedSize = (size + 3) & ~3;
330 size_t newOffset = mAllocOffset + alignedSize;
331 if (newOffset > mSlotsOffset) {
332 maybeInflate();
333 newOffset = mAllocOffset + alignedSize;
334 if (newOffset > mSlotsOffset) {
335 return NO_MEMORY;
336 }
337 }
338 *outOffset = mAllocOffset;
339 mAllocOffset = newOffset;
340 return OK;
341 }
342
getFieldSlot(uint32_t row,uint32_t column)343 CursorWindow::FieldSlot* CursorWindow::getFieldSlot(uint32_t row, uint32_t column) {
344 // This is carefully tuned to use as few cycles as
345 // possible, since this is an extremely hot code path;
346 // see CursorWindow_bench.cpp for more details
347 void *result = static_cast<uint8_t*>(mSlotsStart)
348 - (((row * mNumColumns) + column) << kSlotShift);
349 if (result < mSlotsEnd || result > mSlotsStart || column >= mNumColumns) {
350 LOG(ERROR) << "Failed to read row " << row << ", column " << column
351 << " from a window with " << mNumRows << " rows, " << mNumColumns << " columns";
352 return nullptr;
353 } else {
354 return static_cast<FieldSlot*>(result);
355 }
356 }
357
putBlob(uint32_t row,uint32_t column,const void * value,size_t size)358 status_t CursorWindow::putBlob(uint32_t row, uint32_t column, const void* value, size_t size) {
359 return putBlobOrString(row, column, value, size, FIELD_TYPE_BLOB);
360 }
361
putString(uint32_t row,uint32_t column,const char * value,size_t sizeIncludingNull)362 status_t CursorWindow::putString(uint32_t row, uint32_t column, const char* value,
363 size_t sizeIncludingNull) {
364 return putBlobOrString(row, column, value, sizeIncludingNull, FIELD_TYPE_STRING);
365 }
366
putBlobOrString(uint32_t row,uint32_t column,const void * value,size_t size,int32_t type)367 status_t CursorWindow::putBlobOrString(uint32_t row, uint32_t column,
368 const void* value, size_t size, int32_t type) {
369 if (mReadOnly) {
370 return INVALID_OPERATION;
371 }
372
373 FieldSlot* fieldSlot = getFieldSlot(row, column);
374 if (!fieldSlot) {
375 return BAD_VALUE;
376 }
377
378 uint32_t offset;
379 if (alloc(size, &offset)) {
380 return NO_MEMORY;
381 }
382
383 memcpy(offsetToPtr(offset), value, size);
384
385 fieldSlot = getFieldSlot(row, column);
386 fieldSlot->type = type;
387 fieldSlot->data.buffer.offset = offset;
388 fieldSlot->data.buffer.size = size;
389 return OK;
390 }
391
putLong(uint32_t row,uint32_t column,int64_t value)392 status_t CursorWindow::putLong(uint32_t row, uint32_t column, int64_t value) {
393 if (mReadOnly) {
394 return INVALID_OPERATION;
395 }
396
397 FieldSlot* fieldSlot = getFieldSlot(row, column);
398 if (!fieldSlot) {
399 return BAD_VALUE;
400 }
401
402 fieldSlot->type = FIELD_TYPE_INTEGER;
403 fieldSlot->data.l = value;
404 return OK;
405 }
406
putDouble(uint32_t row,uint32_t column,double value)407 status_t CursorWindow::putDouble(uint32_t row, uint32_t column, double value) {
408 if (mReadOnly) {
409 return INVALID_OPERATION;
410 }
411
412 FieldSlot* fieldSlot = getFieldSlot(row, column);
413 if (!fieldSlot) {
414 return BAD_VALUE;
415 }
416
417 fieldSlot->type = FIELD_TYPE_FLOAT;
418 fieldSlot->data.d = value;
419 return OK;
420 }
421
putNull(uint32_t row,uint32_t column)422 status_t CursorWindow::putNull(uint32_t row, uint32_t column) {
423 if (mReadOnly) {
424 return INVALID_OPERATION;
425 }
426
427 FieldSlot* fieldSlot = getFieldSlot(row, column);
428 if (!fieldSlot) {
429 return BAD_VALUE;
430 }
431
432 fieldSlot->type = FIELD_TYPE_NULL;
433 fieldSlot->data.buffer.offset = 0;
434 fieldSlot->data.buffer.size = 0;
435 return OK;
436 }
437
438 }; // namespace android
439