/* * Copyright 2018, 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. */ #include namespace android { namespace conversion { // native_handle_t helper functions. /** * \brief Take an fd and create a native handle containing only the given fd. * The created handle will need to be deleted manually with * `native_handle_delete()`. * * \param[in] fd The source file descriptor (of type `int`). * \return The create `native_handle_t*` that contains the given \p fd. If the * supplied \p fd is negative, the created native handle will contain no file * descriptors. * * If the native handle cannot be created, the return value will be * `nullptr`. * * This function does not duplicate the file descriptor. */ native_handle_t* native_handle_create_from_fd(int fd) { if (fd < 2) { return native_handle_create(0, 0); } native_handle_t* nh = native_handle_create(1, 0); if (nh == nullptr) { return nullptr; } nh->data[0] = fd; return nh; } /** * \brief Extract a file descriptor from a native handle. * * \param[in] nh The source `native_handle_t*`. * \param[in] index The index of the file descriptor in \p nh to read from. This * input has the default value of `0`. * \return The `index`-th file descriptor in \p nh. If \p nh does not have * enough file descriptors, the returned value will be `-1`. * * This function does not duplicate the file descriptor. */ int native_handle_read_fd(native_handle_t const* nh, int index) { return ((nh == nullptr) || (nh->numFds == 0) || (nh->numFds <= index) || (index < 0)) ? -1 : nh->data[index]; } /** * Conversion functions * ==================== * * There are two main directions of conversion: * - `inTargetType(...)`: Create a wrapper whose lifetime depends on the * input. The wrapper has type `TargetType`. * - `toTargetType(...)`: Create a standalone object of type `TargetType` that * corresponds to the input. The lifetime of the output does not depend on the * lifetime of the input. * - `wrapIn(TargetType*, ...)`: Same as `inTargetType()`, but for `TargetType` * that cannot be copied and/or moved efficiently, or when there are multiple * output arguments. * - `convertTo(TargetType*, ...)`: Same as `toTargetType()`, but for * `TargetType` that cannot be copied and/or moved efficiently, or when there * are multiple output arguments. * * `wrapIn()` and `convertTo()` functions will take output arguments before * input arguments. Some of these functions might return a value to indicate * success or error. * * In converting or wrapping something as a Treble type that contains a * `hidl_handle`, `native_handle_t*` will need to be created and returned as * an additional output argument, hence only `wrapIn()` or `convertTo()` would * be available. The caller must call `native_handle_delete()` to deallocate the * returned native handle when it is no longer needed. * * For types that contain file descriptors, `inTargetType()` and `wrapAs()` do * not perform duplication of file descriptors, while `toTargetType()` and * `convertTo()` do. */ /** * \brief Convert `Return` to `status_t`. This is for legacy binder calls. * * \param[in] t The source `Return`. * \return The corresponding `status_t`. */ // convert: Return -> status_t status_t toStatusT(Return const& t) { return t.isOk() ? OK : (t.isDeadObject() ? DEAD_OBJECT : UNKNOWN_ERROR); } /** * \brief Wrap `native_handle_t*` in `hidl_handle`. * * \param[in] nh The source `native_handle_t*`. * \return The `hidl_handle` that points to \p nh. */ // wrap: native_handle_t* -> hidl_handle hidl_handle inHidlHandle(native_handle_t const* nh) { return hidl_handle(nh); } /** * \brief Convert `int32_t` to `Dataspace`. * * \param[in] l The source `int32_t`. * \result The corresponding `Dataspace`. */ // convert: int32_t -> Dataspace Dataspace toHardwareDataspace(int32_t l) { return static_cast(l); } /** * \brief Convert `Dataspace` to `int32_t`. * * \param[in] t The source `Dataspace`. * \result The corresponding `int32_t`. */ // convert: Dataspace -> int32_t int32_t toRawDataspace(Dataspace const& t) { return static_cast(t); } /** * \brief Wrap an opaque buffer inside a `hidl_vec`. * * \param[in] l The pointer to the beginning of the opaque buffer. * \param[in] size The size of the buffer. * \return A `hidl_vec` that points to the buffer. */ // wrap: void*, size_t -> hidl_vec hidl_vec inHidlBytes(void const* l, size_t size) { hidl_vec t; t.setToExternal(static_cast(const_cast(l)), size, false); return t; } /** * \brief Create a `hidl_vec` that is a copy of an opaque buffer. * * \param[in] l The pointer to the beginning of the opaque buffer. * \param[in] size The size of the buffer. * \return A `hidl_vec` that is a copy of the input buffer. */ // convert: void*, size_t -> hidl_vec hidl_vec toHidlBytes(void const* l, size_t size) { hidl_vec t; t.resize(size); uint8_t const* src = static_cast(l); std::copy(src, src + size, t.data()); return t; } /** * \brief Wrap `GraphicBuffer` in `AnwBuffer`. * * \param[out] t The wrapper of type `AnwBuffer`. * \param[in] l The source `GraphicBuffer`. */ // wrap: GraphicBuffer -> AnwBuffer void wrapAs(AnwBuffer* t, GraphicBuffer const& l) { t->attr.width = l.getWidth(); t->attr.height = l.getHeight(); t->attr.stride = l.getStride(); t->attr.format = static_cast(l.getPixelFormat()); t->attr.layerCount = l.getLayerCount(); t->attr.usage = static_cast(l.getUsage()); t->attr.id = l.getId(); t->attr.generationNumber = l.getGenerationNumber(); t->nativeHandle = hidl_handle(l.handle); } /** * \brief Convert `AnwBuffer` to `GraphicBuffer`. * * \param[out] l The destination `GraphicBuffer`. * \param[in] t The source `AnwBuffer`. * * This function will duplicate all file descriptors in \p t. */ // convert: AnwBuffer -> GraphicBuffer // Ref: frameworks/native/libs/ui/GraphicBuffer.cpp: GraphicBuffer::flatten bool convertTo(GraphicBuffer* l, AnwBuffer const& t) { native_handle_t* handle = t.nativeHandle == nullptr ? nullptr : native_handle_clone(t.nativeHandle); size_t const numInts = 12 + static_cast(handle ? handle->numInts : 0); int32_t* ints = new int32_t[numInts]; size_t numFds = static_cast(handle ? handle->numFds : 0); int* fds = new int[numFds]; ints[0] = 'GBFR'; ints[1] = static_cast(t.attr.width); ints[2] = static_cast(t.attr.height); ints[3] = static_cast(t.attr.stride); ints[4] = static_cast(t.attr.format); ints[5] = static_cast(t.attr.layerCount); ints[6] = static_cast(t.attr.usage); ints[7] = static_cast(t.attr.id >> 32); ints[8] = static_cast(t.attr.id & 0xFFFFFFFF); ints[9] = static_cast(t.attr.generationNumber); ints[10] = 0; ints[11] = 0; if (handle) { ints[10] = static_cast(handle->numFds); ints[11] = static_cast(handle->numInts); int* intsStart = handle->data + handle->numFds; std::copy(handle->data, intsStart, fds); std::copy(intsStart, intsStart + handle->numInts, &ints[12]); } void const* constBuffer = static_cast(ints); size_t size = numInts * sizeof(int32_t); int const* constFds = static_cast(fds); status_t status = l->unflatten(constBuffer, size, constFds, numFds); delete [] fds; delete [] ints; native_handle_delete(handle); return status == NO_ERROR; } /** * Conversion functions for types outside media * ============================================ * * Some objects in libui and libgui that were made to go through binder calls do * not expose ways to read or write their fields to the public. To pass an * object of this kind through the HIDL boundary, translation functions need to * work around the access restriction by using the publicly available * `flatten()` and `unflatten()` functions. * * All `flatten()` and `unflatten()` overloads follow the same convention as * follows: * * status_t flatten(ObjectType const& object, * [OtherType const& other, ...] * void*& buffer, size_t& size, * int*& fds, size_t& numFds) * * status_t unflatten(ObjectType* object, * [OtherType* other, ...,] * void*& buffer, size_t& size, * int*& fds, size_t& numFds) * * The number of `other` parameters varies depending on the `ObjectType`. For * example, in the process of unflattening an object that contains * `hidl_handle`, `other` is needed to hold `native_handle_t` objects that will * be created. * * The last four parameters always work the same way in all overloads of * `flatten()` and `unflatten()`: * - For `flatten()`, `buffer` is the pointer to the non-fd buffer to be filled, * `size` is the size (in bytes) of the non-fd buffer pointed to by `buffer`, * `fds` is the pointer to the fd buffer to be filled, and `numFds` is the * size (in ints) of the fd buffer pointed to by `fds`. * - For `unflatten()`, `buffer` is the pointer to the non-fd buffer to be read * from, `size` is the size (in bytes) of the non-fd buffer pointed to by * `buffer`, `fds` is the pointer to the fd buffer to be read from, and * `numFds` is the size (in ints) of the fd buffer pointed to by `fds`. * - After a successful call to `flatten()` or `unflatten()`, `buffer` and `fds` * will be advanced, while `size` and `numFds` will be decreased to reflect * how much storage/data of the two buffers (fd and non-fd) have been used. * - After an unsuccessful call, the values of `buffer`, `size`, `fds` and * `numFds` are invalid. * * The return value of a successful `flatten()` or `unflatten()` call will be * `OK` (also aliased as `NO_ERROR`). Any other values indicate a failure. * * For each object type that supports flattening, there will be two accompanying * functions: `getFlattenedSize()` and `getFdCount()`. `getFlattenedSize()` will * return the size of the non-fd buffer that the object will need for * flattening. `getFdCount()` will return the size of the fd buffer that the * object will need for flattening. * * The set of these four functions, `getFlattenedSize()`, `getFdCount()`, * `flatten()` and `unflatten()`, are similar to functions of the same name in * the abstract class `Flattenable`. The only difference is that functions in * this file are not member functions of the object type. For example, we write * * flatten(x, buffer, size, fds, numFds) * * instead of * * x.flatten(buffer, size, fds, numFds) * * because we cannot modify the type of `x`. * * There is one exception to the naming convention: `hidl_handle` that * represents a fence. The four functions for this "Fence" type have the word * "Fence" attched to their names because the object type, which is * `hidl_handle`, does not carry the special meaning that the object itself can * only contain zero or one file descriptor. */ // Ref: frameworks/native/libs/ui/Fence.cpp /** * \brief Return the size of the non-fd buffer required to flatten a fence. * * \param[in] fence The input fence of type `hidl_handle`. * \return The required size of the flat buffer. * * The current version of this function always returns 4, which is the number of * bytes required to store the number of file descriptors contained in the fd * part of the flat buffer. */ size_t getFenceFlattenedSize(hidl_handle const& /* fence */) { return 4; }; /** * \brief Return the number of file descriptors contained in a fence. * * \param[in] fence The input fence of type `hidl_handle`. * \return `0` if \p fence does not contain a valid file descriptor, or `1` * otherwise. */ size_t getFenceFdCount(hidl_handle const& fence) { return native_handle_read_fd(fence) == -1 ? 0 : 1; } /** * \brief Unflatten `Fence` to `hidl_handle`. * * \param[out] fence The destination `hidl_handle`. * \param[out] nh The underlying native handle. * \param[in,out] buffer The pointer to the flat non-fd buffer. * \param[in,out] size The size of the flat non-fd buffer. * \param[in,out] fds The pointer to the flat fd buffer. * \param[in,out] numFds The size of the flat fd buffer. * \return `NO_ERROR` on success; other value on failure. * * If the return value is `NO_ERROR`, \p nh will point to a newly created * native handle, which needs to be deleted with `native_handle_delete()` * afterwards. */ status_t unflattenFence(hidl_handle* fence, native_handle_t** nh, void const*& buffer, size_t& size, int const*& fds, size_t& numFds) { if (size < 4) { return NO_MEMORY; } uint32_t numFdsInHandle; FlattenableUtils::read(buffer, size, numFdsInHandle); if (numFdsInHandle > 1) { return BAD_VALUE; } if (numFds < numFdsInHandle) { return NO_MEMORY; } if (numFdsInHandle) { *nh = native_handle_create_from_fd(*fds); if (*nh == nullptr) { return NO_MEMORY; } *fence = *nh; ++fds; --numFds; } else { *nh = nullptr; *fence = hidl_handle(); } return NO_ERROR; } /** * \brief Flatten `hidl_handle` as `Fence`. * * \param[in] t The source `hidl_handle`. * \param[in,out] buffer The pointer to the flat non-fd buffer. * \param[in,out] size The size of the flat non-fd buffer. * \param[in,out] fds The pointer to the flat fd buffer. * \param[in,out] numFds The size of the flat fd buffer. * \return `NO_ERROR` on success; other value on failure. */ status_t flattenFence(hidl_handle const& fence, void*& buffer, size_t& size, int*& fds, size_t& numFds) { if (size < getFenceFlattenedSize(fence) || numFds < getFenceFdCount(fence)) { return NO_MEMORY; } // Cast to uint32_t since the size of a size_t can vary between 32- and // 64-bit processes FlattenableUtils::write(buffer, size, static_cast(getFenceFdCount(fence))); int fd = native_handle_read_fd(fence); if (fd != -1) { *fds = fd; ++fds; --numFds; } return NO_ERROR; } /** * \brief Wrap `Fence` in `hidl_handle`. * * \param[out] t The wrapper of type `hidl_handle`. * \param[out] nh The native handle pointed to by \p t. * \param[in] l The source `Fence`. * * On success, \p nh will hold a newly created native handle, which must be * deleted manually with `native_handle_delete()` afterwards. */ // wrap: Fence -> hidl_handle bool wrapAs(hidl_handle* t, native_handle_t** nh, Fence const& l) { size_t const baseSize = l.getFlattenedSize(); std::unique_ptr baseBuffer( new (std::nothrow) uint8_t[baseSize]); if (!baseBuffer) { return false; } size_t const baseNumFds = l.getFdCount(); std::unique_ptr baseFds( new (std::nothrow) int[baseNumFds]); if (!baseFds) { return false; } void* buffer = static_cast(baseBuffer.get()); size_t size = baseSize; int* fds = static_cast(baseFds.get()); size_t numFds = baseNumFds; if (l.flatten(buffer, size, fds, numFds) != NO_ERROR) { return false; } void const* constBuffer = static_cast(baseBuffer.get()); size = baseSize; int const* constFds = static_cast(baseFds.get()); numFds = baseNumFds; if (unflattenFence(t, nh, constBuffer, size, constFds, numFds) != NO_ERROR) { return false; } return true; } /** * \brief Convert `hidl_handle` to `Fence`. * * \param[out] l The destination `Fence`. `l` must not have been used * (`l->isValid()` must return `false`) before this function is called. * \param[in] t The source `hidl_handle`. * * If \p t contains a valid file descriptor, it will be duplicated. */ // convert: hidl_handle -> Fence bool convertTo(Fence* l, hidl_handle const& t) { int fd = native_handle_read_fd(t); if (fd != -1) { fd = dup(fd); if (fd == -1) { return false; } } native_handle_t* nh = native_handle_create_from_fd(fd); if (nh == nullptr) { if (fd != -1) { close(fd); } return false; } size_t const baseSize = getFenceFlattenedSize(t); std::unique_ptr baseBuffer( new (std::nothrow) uint8_t[baseSize]); if (!baseBuffer) { native_handle_delete(nh); return false; } size_t const baseNumFds = getFenceFdCount(t); std::unique_ptr baseFds( new (std::nothrow) int[baseNumFds]); if (!baseFds) { native_handle_delete(nh); return false; } void* buffer = static_cast(baseBuffer.get()); size_t size = baseSize; int* fds = static_cast(baseFds.get()); size_t numFds = baseNumFds; if (flattenFence(hidl_handle(nh), buffer, size, fds, numFds) != NO_ERROR) { native_handle_delete(nh); return false; } native_handle_delete(nh); void const* constBuffer = static_cast(baseBuffer.get()); size = baseSize; int const* constFds = static_cast(baseFds.get()); numFds = baseNumFds; if (l->unflatten(constBuffer, size, constFds, numFds) != NO_ERROR) { return false; } return true; } // Ref: frameworks/native/libs/ui/FenceTime.cpp: FenceTime::Snapshot /** * \brief Return the size of the non-fd buffer required to flatten * `FenceTimeSnapshot`. * * \param[in] t The input `FenceTimeSnapshot`. * \return The required size of the flat buffer. */ size_t getFlattenedSize( HGraphicBufferProducer::FenceTimeSnapshot const& t) { constexpr size_t min = sizeof(t.state); switch (t.state) { case HGraphicBufferProducer::FenceTimeSnapshot::State::EMPTY: return min; case HGraphicBufferProducer::FenceTimeSnapshot::State::FENCE: return min + getFenceFlattenedSize(t.fence); case HGraphicBufferProducer::FenceTimeSnapshot::State::SIGNAL_TIME: return min + sizeof( ::android::FenceTime::Snapshot::signalTime); } return 0; } /** * \brief Return the number of file descriptors contained in * `FenceTimeSnapshot`. * * \param[in] t The input `FenceTimeSnapshot`. * \return The number of file descriptors contained in \p snapshot. */ size_t getFdCount( HGraphicBufferProducer::FenceTimeSnapshot const& t) { return t.state == HGraphicBufferProducer::FenceTimeSnapshot::State::FENCE ? getFenceFdCount(t.fence) : 0; } /** * \brief Flatten `FenceTimeSnapshot`. * * \param[in] t The source `FenceTimeSnapshot`. * \param[in,out] buffer The pointer to the flat non-fd buffer. * \param[in,out] size The size of the flat non-fd buffer. * \param[in,out] fds The pointer to the flat fd buffer. * \param[in,out] numFds The size of the flat fd buffer. * \return `NO_ERROR` on success; other value on failure. * * This function will duplicate the file descriptor in `t.fence` if `t.state == * FENCE`. */ status_t flatten(HGraphicBufferProducer::FenceTimeSnapshot const& t, void*& buffer, size_t& size, int*& fds, size_t& numFds) { if (size < getFlattenedSize(t)) { return NO_MEMORY; } switch (t.state) { case HGraphicBufferProducer::FenceTimeSnapshot::State::EMPTY: FlattenableUtils::write(buffer, size, ::android::FenceTime::Snapshot::State::EMPTY); return NO_ERROR; case HGraphicBufferProducer::FenceTimeSnapshot::State::FENCE: FlattenableUtils::write(buffer, size, ::android::FenceTime::Snapshot::State::FENCE); return flattenFence(t.fence, buffer, size, fds, numFds); case HGraphicBufferProducer::FenceTimeSnapshot::State::SIGNAL_TIME: FlattenableUtils::write(buffer, size, ::android::FenceTime::Snapshot::State::SIGNAL_TIME); FlattenableUtils::write(buffer, size, t.signalTimeNs); return NO_ERROR; } return NO_ERROR; } /** * \brief Unflatten `FenceTimeSnapshot`. * * \param[out] t The destination `FenceTimeSnapshot`. * \param[out] nh The underlying native handle. * \param[in,out] buffer The pointer to the flat non-fd buffer. * \param[in,out] size The size of the flat non-fd buffer. * \param[in,out] fds The pointer to the flat fd buffer. * \param[in,out] numFds The size of the flat fd buffer. * \return `NO_ERROR` on success; other value on failure. * * If the return value is `NO_ERROR` and the constructed snapshot contains a * file descriptor, \p nh will be created to hold that file descriptor. In this * case, \p nh needs to be deleted with `native_handle_delete()` afterwards. */ status_t unflatten( HGraphicBufferProducer::FenceTimeSnapshot* t, native_handle_t** nh, void const*& buffer, size_t& size, int const*& fds, size_t& numFds) { if (size < sizeof(t->state)) { return NO_MEMORY; } *nh = nullptr; ::android::FenceTime::Snapshot::State state; FlattenableUtils::read(buffer, size, state); switch (state) { case ::android::FenceTime::Snapshot::State::EMPTY: t->state = HGraphicBufferProducer::FenceTimeSnapshot::State::EMPTY; return NO_ERROR; case ::android::FenceTime::Snapshot::State::FENCE: t->state = HGraphicBufferProducer::FenceTimeSnapshot::State::FENCE; return unflattenFence(&t->fence, nh, buffer, size, fds, numFds); case ::android::FenceTime::Snapshot::State::SIGNAL_TIME: t->state = HGraphicBufferProducer::FenceTimeSnapshot::State::SIGNAL_TIME; if (size < sizeof(t->signalTimeNs)) { return NO_MEMORY; } FlattenableUtils::read(buffer, size, t->signalTimeNs); return NO_ERROR; } return NO_ERROR; } // Ref: frameworks/native/libs/gui/FrameTimestamps.cpp: FrameEventsDelta /** * \brief Return a lower bound on the size of the non-fd buffer required to * flatten `FrameEventsDelta`. * * \param[in] t The input `FrameEventsDelta`. * \return A lower bound on the size of the flat buffer. */ constexpr size_t minFlattenedSize( HGraphicBufferProducer::FrameEventsDelta const& /* t */) { return sizeof(uint64_t) + // mFrameNumber sizeof(uint8_t) + // mIndex sizeof(uint8_t) + // mAddPostCompositeCalled sizeof(uint8_t) + // mAddRetireCalled sizeof(uint8_t) + // mAddReleaseCalled sizeof(nsecs_t) + // mPostedTime sizeof(nsecs_t) + // mRequestedPresentTime sizeof(nsecs_t) + // mLatchTime sizeof(nsecs_t) + // mFirstRefreshStartTime sizeof(nsecs_t); // mLastRefreshStartTime } /** * \brief Return the size of the non-fd buffer required to flatten * `FrameEventsDelta`. * * \param[in] t The input `FrameEventsDelta`. * \return The required size of the flat buffer. */ size_t getFlattenedSize( HGraphicBufferProducer::FrameEventsDelta const& t) { return minFlattenedSize(t) + getFlattenedSize(t.gpuCompositionDoneFence) + getFlattenedSize(t.displayPresentFence) + getFlattenedSize(t.displayRetireFence) + getFlattenedSize(t.releaseFence); }; /** * \brief Return the number of file descriptors contained in * `FrameEventsDelta`. * * \param[in] t The input `FrameEventsDelta`. * \return The number of file descriptors contained in \p t. */ size_t getFdCount( HGraphicBufferProducer::FrameEventsDelta const& t) { return getFdCount(t.gpuCompositionDoneFence) + getFdCount(t.displayPresentFence) + getFdCount(t.displayRetireFence) + getFdCount(t.releaseFence); }; /** * \brief Unflatten `FrameEventsDelta`. * * \param[out] t The destination `FrameEventsDelta`. * \param[out] nh The underlying array of native handles. * \param[in,out] buffer The pointer to the flat non-fd buffer. * \param[in,out] size The size of the flat non-fd buffer. * \param[in,out] fds The pointer to the flat fd buffer. * \param[in,out] numFds The size of the flat fd buffer. * \return `NO_ERROR` on success; other value on failure. * * If the return value is `NO_ERROR`, \p nh will have length 4, and it will be * populated with `nullptr` or newly created handles. Each non-null slot in \p * nh will need to be deleted manually with `native_handle_delete()`. */ status_t unflatten(HGraphicBufferProducer::FrameEventsDelta* t, std::vector* nh, void const*& buffer, size_t& size, int const*& fds, size_t& numFds) { if (size < minFlattenedSize(*t)) { return NO_MEMORY; } FlattenableUtils::read(buffer, size, t->frameNumber); // These were written as uint8_t for alignment. uint8_t temp = 0; FlattenableUtils::read(buffer, size, temp); size_t index = static_cast(temp); if (index >= ::android::FrameEventHistory::MAX_FRAME_HISTORY) { return BAD_VALUE; } t->index = static_cast(index); FlattenableUtils::read(buffer, size, temp); t->addPostCompositeCalled = static_cast(temp); FlattenableUtils::read(buffer, size, temp); t->addRetireCalled = static_cast(temp); FlattenableUtils::read(buffer, size, temp); t->addReleaseCalled = static_cast(temp); FlattenableUtils::read(buffer, size, t->postedTimeNs); FlattenableUtils::read(buffer, size, t->requestedPresentTimeNs); FlattenableUtils::read(buffer, size, t->latchTimeNs); FlattenableUtils::read(buffer, size, t->firstRefreshStartTimeNs); FlattenableUtils::read(buffer, size, t->lastRefreshStartTimeNs); FlattenableUtils::read(buffer, size, t->dequeueReadyTime); // Fences HGraphicBufferProducer::FenceTimeSnapshot* tSnapshot[4]; tSnapshot[0] = &t->gpuCompositionDoneFence; tSnapshot[1] = &t->displayPresentFence; tSnapshot[2] = &t->displayRetireFence; tSnapshot[3] = &t->releaseFence; nh->resize(4); for (size_t snapshotIndex = 0; snapshotIndex < 4; ++snapshotIndex) { status_t status = unflatten( tSnapshot[snapshotIndex], &((*nh)[snapshotIndex]), buffer, size, fds, numFds); if (status != NO_ERROR) { while (snapshotIndex > 0) { --snapshotIndex; if ((*nh)[snapshotIndex] != nullptr) { native_handle_delete((*nh)[snapshotIndex]); } } return status; } } return NO_ERROR; } /** * \brief Flatten `FrameEventsDelta`. * * \param[in] t The source `FrameEventsDelta`. * \param[in,out] buffer The pointer to the flat non-fd buffer. * \param[in,out] size The size of the flat non-fd buffer. * \param[in,out] fds The pointer to the flat fd buffer. * \param[in,out] numFds The size of the flat fd buffer. * \return `NO_ERROR` on success; other value on failure. * * This function will duplicate file descriptors contained in \p t. */ // Ref: frameworks/native/libs/gui/FrameTimestamp.cpp: // FrameEventsDelta::flatten status_t flatten(HGraphicBufferProducer::FrameEventsDelta const& t, void*& buffer, size_t& size, int*& fds, size_t numFds) { // Check that t.index is within a valid range. if (t.index >= static_cast(FrameEventHistory::MAX_FRAME_HISTORY) || t.index > std::numeric_limits::max()) { return BAD_VALUE; } FlattenableUtils::write(buffer, size, t.frameNumber); // These are static_cast to uint8_t for alignment. FlattenableUtils::write(buffer, size, static_cast(t.index)); FlattenableUtils::write( buffer, size, static_cast(t.addPostCompositeCalled)); FlattenableUtils::write( buffer, size, static_cast(t.addRetireCalled)); FlattenableUtils::write( buffer, size, static_cast(t.addReleaseCalled)); FlattenableUtils::write(buffer, size, t.postedTimeNs); FlattenableUtils::write(buffer, size, t.requestedPresentTimeNs); FlattenableUtils::write(buffer, size, t.latchTimeNs); FlattenableUtils::write(buffer, size, t.firstRefreshStartTimeNs); FlattenableUtils::write(buffer, size, t.lastRefreshStartTimeNs); FlattenableUtils::write(buffer, size, t.dequeueReadyTime); // Fences HGraphicBufferProducer::FenceTimeSnapshot const* tSnapshot[4]; tSnapshot[0] = &t.gpuCompositionDoneFence; tSnapshot[1] = &t.displayPresentFence; tSnapshot[2] = &t.displayRetireFence; tSnapshot[3] = &t.releaseFence; for (size_t snapshotIndex = 0; snapshotIndex < 4; ++snapshotIndex) { status_t status = flatten( *(tSnapshot[snapshotIndex]), buffer, size, fds, numFds); if (status != NO_ERROR) { return status; } } return NO_ERROR; } // Ref: frameworks/native/libs/gui/FrameTimestamps.cpp: FrameEventHistoryDelta /** * \brief Return the size of the non-fd buffer required to flatten * `HGraphicBufferProducer::FrameEventHistoryDelta`. * * \param[in] t The input `HGraphicBufferProducer::FrameEventHistoryDelta`. * \return The required size of the flat buffer. */ size_t getFlattenedSize( HGraphicBufferProducer::FrameEventHistoryDelta const& t) { size_t size = 4 + // mDeltas.size() sizeof(t.compositorTiming); for (size_t i = 0; i < t.deltas.size(); ++i) { size += getFlattenedSize(t.deltas[i]); } return size; } /** * \brief Return the number of file descriptors contained in * `HGraphicBufferProducer::FrameEventHistoryDelta`. * * \param[in] t The input `HGraphicBufferProducer::FrameEventHistoryDelta`. * \return The number of file descriptors contained in \p t. */ size_t getFdCount( HGraphicBufferProducer::FrameEventHistoryDelta const& t) { size_t numFds = 0; for (size_t i = 0; i < t.deltas.size(); ++i) { numFds += getFdCount(t.deltas[i]); } return numFds; } /** * \brief Unflatten `FrameEventHistoryDelta`. * * \param[out] t The destination `FrameEventHistoryDelta`. * \param[out] nh The underlying array of arrays of native handles. * \param[in,out] buffer The pointer to the flat non-fd buffer. * \param[in,out] size The size of the flat non-fd buffer. * \param[in,out] fds The pointer to the flat fd buffer. * \param[in,out] numFds The size of the flat fd buffer. * \return `NO_ERROR` on success; other value on failure. * * If the return value is `NO_ERROR`, \p nh will be populated with `nullptr` or * newly created handles. The second dimension of \p nh will be 4. Each non-null * slot in \p nh will need to be deleted manually with `native_handle_delete()`. */ status_t unflatten( HGraphicBufferProducer::FrameEventHistoryDelta* t, std::vector >* nh, void const*& buffer, size_t& size, int const*& fds, size_t& numFds) { if (size < 4) { return NO_MEMORY; } FlattenableUtils::read(buffer, size, t->compositorTiming); uint32_t deltaCount = 0; FlattenableUtils::read(buffer, size, deltaCount); if (static_cast(deltaCount) > ::android::FrameEventHistory::MAX_FRAME_HISTORY) { return BAD_VALUE; } t->deltas.resize(deltaCount); nh->resize(deltaCount); for (size_t deltaIndex = 0; deltaIndex < deltaCount; ++deltaIndex) { status_t status = unflatten( &(t->deltas[deltaIndex]), &((*nh)[deltaIndex]), buffer, size, fds, numFds); if (status != NO_ERROR) { return status; } } return NO_ERROR; } /** * \brief Flatten `FrameEventHistoryDelta`. * * \param[in] t The source `FrameEventHistoryDelta`. * \param[in,out] buffer The pointer to the flat non-fd buffer. * \param[in,out] size The size of the flat non-fd buffer. * \param[in,out] fds The pointer to the flat fd buffer. * \param[in,out] numFds The size of the flat fd buffer. * \return `NO_ERROR` on success; other value on failure. * * This function will duplicate file descriptors contained in \p t. */ status_t flatten( HGraphicBufferProducer::FrameEventHistoryDelta const& t, void*& buffer, size_t& size, int*& fds, size_t& numFds) { if (t.deltas.size() > ::android::FrameEventHistory::MAX_FRAME_HISTORY) { return BAD_VALUE; } if (size < getFlattenedSize(t)) { return NO_MEMORY; } FlattenableUtils::write(buffer, size, t.compositorTiming); FlattenableUtils::write(buffer, size, static_cast(t.deltas.size())); for (size_t deltaIndex = 0; deltaIndex < t.deltas.size(); ++deltaIndex) { status_t status = flatten(t.deltas[deltaIndex], buffer, size, fds, numFds); if (status != NO_ERROR) { return status; } } return NO_ERROR; } /** * \brief Wrap `::android::FrameEventHistoryData` in * `HGraphicBufferProducer::FrameEventHistoryDelta`. * * \param[out] t The wrapper of type * `HGraphicBufferProducer::FrameEventHistoryDelta`. * \param[out] nh The array of array of native handles that are referred to by * members of \p t. * \param[in] l The source `::android::FrameEventHistoryDelta`. * * On success, each member of \p nh will be either `nullptr` or a newly created * native handle. All the non-`nullptr` elements must be deleted individually * with `native_handle_delete()`. */ bool wrapAs(HGraphicBufferProducer::FrameEventHistoryDelta* t, std::vector >* nh, ::android::FrameEventHistoryDelta const& l) { size_t const baseSize = l.getFlattenedSize(); std::unique_ptr baseBuffer( new (std::nothrow) uint8_t[baseSize]); if (!baseBuffer) { return false; } size_t const baseNumFds = l.getFdCount(); std::unique_ptr baseFds( new (std::nothrow) int[baseNumFds]); if (!baseFds) { return false; } void* buffer = static_cast(baseBuffer.get()); size_t size = baseSize; int* fds = baseFds.get(); size_t numFds = baseNumFds; if (l.flatten(buffer, size, fds, numFds) != NO_ERROR) { return false; } void const* constBuffer = static_cast(baseBuffer.get()); size = baseSize; int const* constFds = static_cast(baseFds.get()); numFds = baseNumFds; if (unflatten(t, nh, constBuffer, size, constFds, numFds) != NO_ERROR) { return false; } return true; } /** * \brief Convert `HGraphicBufferProducer::FrameEventHistoryDelta` to * `::android::FrameEventHistoryDelta`. * * \param[out] l The destination `::android::FrameEventHistoryDelta`. * \param[in] t The source `HGraphicBufferProducer::FrameEventHistoryDelta`. * * This function will duplicate all file descriptors contained in \p t. */ bool convertTo( ::android::FrameEventHistoryDelta* l, HGraphicBufferProducer::FrameEventHistoryDelta const& t) { size_t const baseSize = getFlattenedSize(t); std::unique_ptr baseBuffer( new (std::nothrow) uint8_t[baseSize]); if (!baseBuffer) { return false; } size_t const baseNumFds = getFdCount(t); std::unique_ptr baseFds( new (std::nothrow) int[baseNumFds]); if (!baseFds) { return false; } void* buffer = static_cast(baseBuffer.get()); size_t size = baseSize; int* fds = static_cast(baseFds.get()); size_t numFds = baseNumFds; if (flatten(t, buffer, size, fds, numFds) != NO_ERROR) { return false; } void const* constBuffer = static_cast(baseBuffer.get()); size = baseSize; int const* constFds = static_cast(baseFds.get()); numFds = baseNumFds; if (l->unflatten(constBuffer, size, constFds, numFds) != NO_ERROR) { return false; } return true; } // Ref: frameworks/native/libs/ui/Region.cpp /** * \brief Return the size of the buffer required to flatten `Region`. * * \param[in] t The input `Region`. * \return The required size of the flat buffer. */ size_t getFlattenedSize(Region const& t) { return sizeof(uint32_t) + t.size() * sizeof(::android::Rect); } /** * \brief Unflatten `Region`. * * \param[out] t The destination `Region`. * \param[in,out] buffer The pointer to the flat buffer. * \param[in,out] size The size of the flat buffer. * \return `NO_ERROR` on success; other value on failure. */ status_t unflatten(Region* t, void const*& buffer, size_t& size) { if (size < sizeof(uint32_t)) { return NO_MEMORY; } uint32_t numRects = 0; FlattenableUtils::read(buffer, size, numRects); if (size < numRects * sizeof(Rect)) { return NO_MEMORY; } if (numRects > (UINT32_MAX / sizeof(Rect))) { return NO_MEMORY; } t->resize(numRects); for (size_t r = 0; r < numRects; ++r) { ::android::Rect rect(::android::Rect::EMPTY_RECT); status_t status = rect.unflatten(buffer, size); if (status != NO_ERROR) { return status; } FlattenableUtils::advance(buffer, size, sizeof(rect)); (*t)[r] = Rect{ static_cast(rect.left), static_cast(rect.top), static_cast(rect.right), static_cast(rect.bottom)}; } return NO_ERROR; } /** * \brief Flatten `Region`. * * \param[in] t The source `Region`. * \param[in,out] buffer The pointer to the flat buffer. * \param[in,out] size The size of the flat buffer. * \return `NO_ERROR` on success; other value on failure. */ status_t flatten(Region const& t, void*& buffer, size_t& size) { if (size < getFlattenedSize(t)) { return NO_MEMORY; } FlattenableUtils::write(buffer, size, static_cast(t.size())); for (size_t r = 0; r < t.size(); ++r) { ::android::Rect rect( static_cast(t[r].left), static_cast(t[r].top), static_cast(t[r].right), static_cast(t[r].bottom)); status_t status = rect.flatten(buffer, size); if (status != NO_ERROR) { return status; } FlattenableUtils::advance(buffer, size, sizeof(rect)); } return NO_ERROR; } /** * \brief Convert `::android::Region` to `Region`. * * \param[out] t The destination `Region`. * \param[in] l The source `::android::Region`. */ // convert: ::android::Region -> Region bool convertTo(Region* t, ::android::Region const& l) { size_t const baseSize = l.getFlattenedSize(); std::unique_ptr baseBuffer( new (std::nothrow) uint8_t[baseSize]); if (!baseBuffer) { return false; } void* buffer = static_cast(baseBuffer.get()); size_t size = baseSize; if (l.flatten(buffer, size) != NO_ERROR) { return false; } void const* constBuffer = static_cast(baseBuffer.get()); size = baseSize; if (unflatten(t, constBuffer, size) != NO_ERROR) { return false; } return true; } /** * \brief Convert `Region` to `::android::Region`. * * \param[out] l The destination `::android::Region`. * \param[in] t The source `Region`. */ // convert: Region -> ::android::Region bool convertTo(::android::Region* l, Region const& t) { size_t const baseSize = getFlattenedSize(t); std::unique_ptr baseBuffer( new (std::nothrow) uint8_t[baseSize]); if (!baseBuffer) { return false; } void* buffer = static_cast(baseBuffer.get()); size_t size = baseSize; if (flatten(t, buffer, size) != NO_ERROR) { return false; } void const* constBuffer = static_cast(baseBuffer.get()); size = baseSize; if (l->unflatten(constBuffer, size) != NO_ERROR) { return false; } return true; } // Ref: frameworks/native/libs/gui/BGraphicBufferProducer.cpp: // BGraphicBufferProducer::QueueBufferInput /** * \brief Return a lower bound on the size of the buffer required to flatten * `HGraphicBufferProducer::QueueBufferInput`. * * \param[in] t The input `HGraphicBufferProducer::QueueBufferInput`. * \return A lower bound on the size of the flat buffer. */ constexpr size_t minFlattenedSize( HGraphicBufferProducer::QueueBufferInput const& /* t */) { return sizeof(int64_t) + // timestamp sizeof(int) + // isAutoTimestamp sizeof(android_dataspace) + // dataSpace sizeof(::android::Rect) + // crop sizeof(int) + // scalingMode sizeof(uint32_t) + // transform sizeof(uint32_t) + // stickyTransform sizeof(bool) + // getFrameTimestamps sizeof(int); // slot } /** * \brief Return the size of the buffer required to flatten * `HGraphicBufferProducer::QueueBufferInput`. * * \param[in] t The input `HGraphicBufferProducer::QueueBufferInput`. * \return The required size of the flat buffer. */ size_t getFlattenedSize(HGraphicBufferProducer::QueueBufferInput const& t) { return minFlattenedSize(t) + getFenceFlattenedSize(t.fence) + getFlattenedSize(t.surfaceDamage) + sizeof(HdrMetadata::validTypes); } /** * \brief Return the number of file descriptors contained in * `HGraphicBufferProducer::QueueBufferInput`. * * \param[in] t The input `HGraphicBufferProducer::QueueBufferInput`. * \return The number of file descriptors contained in \p t. */ size_t getFdCount( HGraphicBufferProducer::QueueBufferInput const& t) { return getFenceFdCount(t.fence); } /** * \brief Flatten `HGraphicBufferProducer::QueueBufferInput`. * * \param[in] t The source `HGraphicBufferProducer::QueueBufferInput`. * \param[out] nh The native handle cloned from `t.fence`. * \param[in,out] buffer The pointer to the flat non-fd buffer. * \param[in,out] size The size of the flat non-fd buffer. * \param[in,out] fds The pointer to the flat fd buffer. * \param[in,out] numFds The size of the flat fd buffer. * \return `NO_ERROR` on success; other value on failure. * * This function will duplicate the file descriptor in `t.fence`. */ status_t flatten(HGraphicBufferProducer::QueueBufferInput const& t, native_handle_t** nh, void*& buffer, size_t& size, int*& fds, size_t& numFds) { if (size < getFlattenedSize(t)) { return NO_MEMORY; } FlattenableUtils::write(buffer, size, t.timestamp); FlattenableUtils::write(buffer, size, static_cast(t.isAutoTimestamp)); FlattenableUtils::write(buffer, size, static_cast(t.dataSpace)); FlattenableUtils::write(buffer, size, ::android::Rect( static_cast(t.crop.left), static_cast(t.crop.top), static_cast(t.crop.right), static_cast(t.crop.bottom))); FlattenableUtils::write(buffer, size, static_cast(t.scalingMode)); FlattenableUtils::write(buffer, size, t.transform); FlattenableUtils::write(buffer, size, t.stickyTransform); FlattenableUtils::write(buffer, size, t.getFrameTimestamps); *nh = t.fence.getNativeHandle() == nullptr ? nullptr : native_handle_clone(t.fence); status_t status = flattenFence(hidl_handle(*nh), buffer, size, fds, numFds); if (status != NO_ERROR) { return status; } status = flatten(t.surfaceDamage, buffer, size); if (status != NO_ERROR) { return status; } FlattenableUtils::write(buffer, size, decltype(HdrMetadata::validTypes)(0)); FlattenableUtils::write(buffer, size, -1 /*slot*/); return NO_ERROR; } /** * \brief Unflatten `HGraphicBufferProducer::QueueBufferInput`. * * \param[out] t The destination `HGraphicBufferProducer::QueueBufferInput`. * \param[out] nh The underlying native handle for `t->fence`. * \param[in,out] buffer The pointer to the flat non-fd buffer. * \param[in,out] size The size of the flat non-fd buffer. * \param[in,out] fds The pointer to the flat fd buffer. * \param[in,out] numFds The size of the flat fd buffer. * \return `NO_ERROR` on success; other value on failure. * * If the return value is `NO_ERROR` and `t->fence` contains a valid file * descriptor, \p nh will be a newly created native handle holding that file * descriptor. \p nh needs to be deleted with `native_handle_delete()` * afterwards. */ status_t unflatten( HGraphicBufferProducer::QueueBufferInput* t, native_handle_t** nh, void const*& buffer, size_t& size, int const*& fds, size_t& numFds) { if (size < minFlattenedSize(*t)) { return NO_MEMORY; } FlattenableUtils::read(buffer, size, t->timestamp); int lIsAutoTimestamp; FlattenableUtils::read(buffer, size, lIsAutoTimestamp); t->isAutoTimestamp = static_cast(lIsAutoTimestamp); android_dataspace_t lDataSpace; FlattenableUtils::read(buffer, size, lDataSpace); t->dataSpace = static_cast(lDataSpace); Rect lCrop; FlattenableUtils::read(buffer, size, lCrop); t->crop = Rect{ static_cast(lCrop.left), static_cast(lCrop.top), static_cast(lCrop.right), static_cast(lCrop.bottom)}; int lScalingMode; FlattenableUtils::read(buffer, size, lScalingMode); t->scalingMode = static_cast(lScalingMode); FlattenableUtils::read(buffer, size, t->transform); FlattenableUtils::read(buffer, size, t->stickyTransform); FlattenableUtils::read(buffer, size, t->getFrameTimestamps); status_t status = unflattenFence(&(t->fence), nh, buffer, size, fds, numFds); if (status != NO_ERROR) { return status; } // HdrMetadata and slot ignored return unflatten(&(t->surfaceDamage), buffer, size); } /** * \brief Convert `HGraphicBufferProducer::QueueBufferInput` to * `BGraphicBufferProducer::QueueBufferInput`. * * \param[out] l The destination `BGraphicBufferProducer::QueueBufferInput`. * \param[in] t The source `HGraphicBufferProducer::QueueBufferInput`. * * If `t.fence` has a valid file descriptor, it will be duplicated. */ bool convertTo( BGraphicBufferProducer::QueueBufferInput* l, HGraphicBufferProducer::QueueBufferInput const& t) { size_t const baseSize = getFlattenedSize(t); std::unique_ptr baseBuffer( new (std::nothrow) uint8_t[baseSize]); if (!baseBuffer) { return false; } size_t const baseNumFds = getFdCount(t); std::unique_ptr baseFds( new (std::nothrow) int[baseNumFds]); if (!baseFds) { return false; } void* buffer = static_cast(baseBuffer.get()); size_t size = baseSize; int* fds = baseFds.get(); size_t numFds = baseNumFds; native_handle_t* nh; if (flatten(t, &nh, buffer, size, fds, numFds) != NO_ERROR) { return false; } void const* constBuffer = static_cast(baseBuffer.get()); size = baseSize; int const* constFds = static_cast(baseFds.get()); numFds = baseNumFds; if (l->unflatten(constBuffer, size, constFds, numFds) != NO_ERROR) { if (nh != nullptr) { native_handle_close(nh); native_handle_delete(nh); } return false; } native_handle_delete(nh); return true; } // Ref: frameworks/native/libs/gui/BGraphicBufferProducer.cpp: // BGraphicBufferProducer::QueueBufferOutput /** * \brief Wrap `BGraphicBufferProducer::QueueBufferOutput` in * `HGraphicBufferProducer::QueueBufferOutput`. * * \param[out] t The wrapper of type * `HGraphicBufferProducer::QueueBufferOutput`. * \param[out] nh The array of array of native handles that are referred to by * members of \p t. * \param[in] l The source `BGraphicBufferProducer::QueueBufferOutput`. * * On success, each member of \p nh will be either `nullptr` or a newly created * native handle. All the non-`nullptr` elements must be deleted individually * with `native_handle_delete()`. */ // wrap: BGraphicBufferProducer::QueueBufferOutput -> // HGraphicBufferProducer::QueueBufferOutput bool wrapAs(HGraphicBufferProducer::QueueBufferOutput* t, std::vector >* nh, BGraphicBufferProducer::QueueBufferOutput const& l) { if (!wrapAs(&(t->frameTimestamps), nh, l.frameTimestamps)) { return false; } t->width = l.width; t->height = l.height; t->transformHint = l.transformHint; t->numPendingBuffers = l.numPendingBuffers; t->nextFrameNumber = l.nextFrameNumber; t->bufferReplaced = l.bufferReplaced; return true; } /** * \brief Convert `HGraphicBufferProducer::QueueBufferOutput` to * `BGraphicBufferProducer::QueueBufferOutput`. * * \param[out] l The destination `BGraphicBufferProducer::QueueBufferOutput`. * \param[in] t The source `HGraphicBufferProducer::QueueBufferOutput`. * * This function will duplicate all file descriptors contained in \p t. */ // convert: HGraphicBufferProducer::QueueBufferOutput -> // BGraphicBufferProducer::QueueBufferOutput bool convertTo( BGraphicBufferProducer::QueueBufferOutput* l, HGraphicBufferProducer::QueueBufferOutput const& t) { if (!convertTo(&(l->frameTimestamps), t.frameTimestamps)) { return false; } l->width = t.width; l->height = t.height; l->transformHint = t.transformHint; l->numPendingBuffers = t.numPendingBuffers; l->nextFrameNumber = t.nextFrameNumber; l->bufferReplaced = t.bufferReplaced; return true; } /** * \brief Convert `BGraphicBufferProducer::DisconnectMode` to * `HGraphicBufferProducer::DisconnectMode`. * * \param[in] l The source `BGraphicBufferProducer::DisconnectMode`. * \return The corresponding `HGraphicBufferProducer::DisconnectMode`. */ HGraphicBufferProducer::DisconnectMode toHidlDisconnectMode( BGraphicBufferProducer::DisconnectMode l) { switch (l) { case BGraphicBufferProducer::DisconnectMode::Api: return HGraphicBufferProducer::DisconnectMode::API; case BGraphicBufferProducer::DisconnectMode::AllLocal: return HGraphicBufferProducer::DisconnectMode::ALL_LOCAL; } return HGraphicBufferProducer::DisconnectMode::API; } /** * \brief Convert `HGraphicBufferProducer::DisconnectMode` to * `BGraphicBufferProducer::DisconnectMode`. * * \param[in] l The source `HGraphicBufferProducer::DisconnectMode`. * \return The corresponding `BGraphicBufferProducer::DisconnectMode`. */ BGraphicBufferProducer::DisconnectMode toGuiDisconnectMode( HGraphicBufferProducer::DisconnectMode t) { switch (t) { case HGraphicBufferProducer::DisconnectMode::API: return BGraphicBufferProducer::DisconnectMode::Api; case HGraphicBufferProducer::DisconnectMode::ALL_LOCAL: return BGraphicBufferProducer::DisconnectMode::AllLocal; } return BGraphicBufferProducer::DisconnectMode::Api; } } // namespace conversion } // namespace android