xref: /aosp_15_r20/frameworks/base/libs/androidfw/CursorWindow.cpp (revision d57664e9bc4670b3ecf6748a746a57c557b6bc9e)
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