| /* | 
 |  * Copyright (C) 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 "ReliableSurface.h" | 
 |  | 
 | #include <log/log_main.h> | 
 | #include <private/android/AHardwareBufferHelpers.h> | 
 | // TODO: this should be including apex instead. | 
 | #include <system/window.h> | 
 | #include <vndk/window.h> | 
 |  | 
 | namespace android::uirenderer::renderthread { | 
 |  | 
 | // TODO: Re-enable after addressing more of the TODO's | 
 | // With this disabled we won't have a good up-front signal that the surface is no longer valid, | 
 | // however we can at least handle that reactively post-draw. There's just not a good mechanism | 
 | // to propagate this error back to the caller | 
 | constexpr bool DISABLE_BUFFER_PREFETCH = true; | 
 |  | 
 | ReliableSurface::ReliableSurface(ANativeWindow* window) : mWindow(window) { | 
 |     LOG_ALWAYS_FATAL_IF(!mWindow, "Error, unable to wrap a nullptr"); | 
 |     ANativeWindow_acquire(mWindow); | 
 | } | 
 |  | 
 | ReliableSurface::~ReliableSurface() { | 
 |     clearReservedBuffer(); | 
 |     // Clear out the interceptors for proper hygiene. | 
 |     // As a concrete example, if the underlying ANativeWindow is associated with | 
 |     // an EGLSurface that is still in use, then if we don't clear out the | 
 |     // interceptors then we walk into undefined behavior. | 
 |     ANativeWindow_setCancelBufferInterceptor(mWindow, nullptr, nullptr); | 
 |     ANativeWindow_setDequeueBufferInterceptor(mWindow, nullptr, nullptr); | 
 |     ANativeWindow_setQueueBufferInterceptor(mWindow, nullptr, nullptr); | 
 |     ANativeWindow_setPerformInterceptor(mWindow, nullptr, nullptr); | 
 |     ANativeWindow_setQueryInterceptor(mWindow, nullptr, nullptr); | 
 |     ANativeWindow_release(mWindow); | 
 | } | 
 |  | 
 | void ReliableSurface::init() { | 
 |     int result = ANativeWindow_setCancelBufferInterceptor(mWindow, hook_cancelBuffer, this); | 
 |     LOG_ALWAYS_FATAL_IF(result != NO_ERROR, "Failed to set cancelBuffer interceptor: error = %d", | 
 |                         result); | 
 |  | 
 |     result = ANativeWindow_setDequeueBufferInterceptor(mWindow, hook_dequeueBuffer, this); | 
 |     LOG_ALWAYS_FATAL_IF(result != NO_ERROR, "Failed to set dequeueBuffer interceptor: error = %d", | 
 |                         result); | 
 |  | 
 |     result = ANativeWindow_setQueueBufferInterceptor(mWindow, hook_queueBuffer, this); | 
 |     LOG_ALWAYS_FATAL_IF(result != NO_ERROR, "Failed to set queueBuffer interceptor: error = %d", | 
 |                         result); | 
 |  | 
 |     result = ANativeWindow_setPerformInterceptor(mWindow, hook_perform, this); | 
 |     LOG_ALWAYS_FATAL_IF(result != NO_ERROR, "Failed to set perform interceptor: error = %d", | 
 |                         result); | 
 |  | 
 |     result = ANativeWindow_setQueryInterceptor(mWindow, hook_query, this); | 
 |     LOG_ALWAYS_FATAL_IF(result != NO_ERROR, "Failed to set query interceptor: error = %d", | 
 |                         result); | 
 | } | 
 |  | 
 | int ReliableSurface::reserveNext() { | 
 |     if constexpr (DISABLE_BUFFER_PREFETCH) { | 
 |         return OK; | 
 |     } | 
 |     { | 
 |         std::lock_guard _lock{mMutex}; | 
 |         if (mReservedBuffer) { | 
 |             ALOGW("reserveNext called but there was already a buffer reserved?"); | 
 |             return OK; | 
 |         } | 
 |         if (mBufferQueueState != OK) { | 
 |             return UNKNOWN_ERROR; | 
 |         } | 
 |         if (mHasDequeuedBuffer) { | 
 |             return OK; | 
 |         } | 
 |     } | 
 |  | 
 |     // TODO: Update this to better handle when requested dimensions have changed | 
 |     // Currently the driver does this via query + perform but that's after we've already | 
 |     // reserved a buffer. Should we do that logic instead? Or should we drop | 
 |     // the backing Surface to the ground and go full manual on the IGraphicBufferProducer instead? | 
 |  | 
 |     int fenceFd = -1; | 
 |     ANativeWindowBuffer* buffer = nullptr; | 
 |  | 
 |     // Note that this calls back into our own hooked method. | 
 |     int result = ANativeWindow_dequeueBuffer(mWindow, &buffer, &fenceFd); | 
 |  | 
 |     { | 
 |         std::lock_guard _lock{mMutex}; | 
 |         LOG_ALWAYS_FATAL_IF(mReservedBuffer, "race condition in reserveNext"); | 
 |         mReservedBuffer = buffer; | 
 |         mReservedFenceFd.reset(fenceFd); | 
 |     } | 
 |  | 
 |     return result; | 
 | } | 
 |  | 
 | void ReliableSurface::clearReservedBuffer() { | 
 |     ANativeWindowBuffer* buffer = nullptr; | 
 |     int releaseFd = -1; | 
 |     { | 
 |         std::lock_guard _lock{mMutex}; | 
 |         if (mReservedBuffer) { | 
 |             ALOGW("Reserved buffer %p was never used", mReservedBuffer); | 
 |             buffer = mReservedBuffer; | 
 |             releaseFd = mReservedFenceFd.release(); | 
 |         } | 
 |         mReservedBuffer = nullptr; | 
 |         mReservedFenceFd.reset(); | 
 |         mHasDequeuedBuffer = false; | 
 |     } | 
 |     if (buffer) { | 
 |         // Note that clearReservedBuffer may be reentrant here, so | 
 |         // mReservedBuffer must be cleared once we reach here to avoid recursing | 
 |         // forever. | 
 |         ANativeWindow_cancelBuffer(mWindow, buffer, releaseFd); | 
 |     } | 
 | } | 
 |  | 
 | bool ReliableSurface::isFallbackBuffer(const ANativeWindowBuffer* windowBuffer) const { | 
 |     if (!mScratchBuffer || !windowBuffer) { | 
 |         return false; | 
 |     } | 
 |     ANativeWindowBuffer* scratchBuffer = | 
 |             AHardwareBuffer_to_ANativeWindowBuffer(mScratchBuffer.get()); | 
 |     return windowBuffer == scratchBuffer; | 
 | } | 
 |  | 
 | ANativeWindowBuffer* ReliableSurface::acquireFallbackBuffer(int error) { | 
 |     std::lock_guard _lock{mMutex}; | 
 |     mBufferQueueState = error; | 
 |  | 
 |     if (mScratchBuffer) { | 
 |         return AHardwareBuffer_to_ANativeWindowBuffer(mScratchBuffer.get()); | 
 |     } | 
 |  | 
 |     AHardwareBuffer_Desc desc = AHardwareBuffer_Desc{ | 
 |             .usage = mUsage, | 
 |             .format = mFormat, | 
 |             .width = 1, | 
 |             .height = 1, | 
 |             .layers = 1, | 
 |             .rfu0 = 0, | 
 |             .rfu1 = 0, | 
 |     }; | 
 |  | 
 |     AHardwareBuffer* newBuffer; | 
 |     int result = AHardwareBuffer_allocate(&desc, &newBuffer); | 
 |  | 
 |     if (result != NO_ERROR) { | 
 |         // Allocate failed, that sucks | 
 |         ALOGW("Failed to allocate scratch buffer, error=%d", result); | 
 |         return nullptr; | 
 |     } | 
 |  | 
 |     mScratchBuffer.reset(newBuffer); | 
 |     return AHardwareBuffer_to_ANativeWindowBuffer(newBuffer); | 
 | } | 
 |  | 
 | int ReliableSurface::hook_dequeueBuffer(ANativeWindow* window, | 
 |                                         ANativeWindow_dequeueBufferFn dequeueBuffer, void* data, | 
 |                                         ANativeWindowBuffer** buffer, int* fenceFd) { | 
 |     ReliableSurface* rs = reinterpret_cast<ReliableSurface*>(data); | 
 |     { | 
 |         std::lock_guard _lock{rs->mMutex}; | 
 |         if (rs->mReservedBuffer) { | 
 |             *buffer = rs->mReservedBuffer; | 
 |             *fenceFd = rs->mReservedFenceFd.release(); | 
 |             rs->mReservedBuffer = nullptr; | 
 |             return OK; | 
 |         } | 
 |     } | 
 |  | 
 |     int result = dequeueBuffer(window, buffer, fenceFd); | 
 |     if (result != OK) { | 
 |         ALOGW("dequeueBuffer failed, error = %d; switching to fallback", result); | 
 |         *buffer = rs->acquireFallbackBuffer(result); | 
 |         *fenceFd = -1; | 
 |         return *buffer ? OK : INVALID_OPERATION; | 
 |     } else { | 
 |         std::lock_guard _lock{rs->mMutex}; | 
 |         rs->mHasDequeuedBuffer = true; | 
 |     } | 
 |     return OK; | 
 | } | 
 |  | 
 | int ReliableSurface::hook_cancelBuffer(ANativeWindow* window, | 
 |                                        ANativeWindow_cancelBufferFn cancelBuffer, void* data, | 
 |                                        ANativeWindowBuffer* buffer, int fenceFd) { | 
 |     ReliableSurface* rs = reinterpret_cast<ReliableSurface*>(data); | 
 |     rs->clearReservedBuffer(); | 
 |     if (rs->isFallbackBuffer(buffer)) { | 
 |         if (fenceFd > 0) { | 
 |             close(fenceFd); | 
 |         } | 
 |         return OK; | 
 |     } | 
 |     return cancelBuffer(window, buffer, fenceFd); | 
 | } | 
 |  | 
 | int ReliableSurface::hook_queueBuffer(ANativeWindow* window, | 
 |                                       ANativeWindow_queueBufferFn queueBuffer, void* data, | 
 |                                       ANativeWindowBuffer* buffer, int fenceFd) { | 
 |     ReliableSurface* rs = reinterpret_cast<ReliableSurface*>(data); | 
 |     rs->clearReservedBuffer(); | 
 |  | 
 |     if (rs->isFallbackBuffer(buffer)) { | 
 |         if (fenceFd > 0) { | 
 |             close(fenceFd); | 
 |         } | 
 |         return OK; | 
 |     } | 
 |  | 
 |     return queueBuffer(window, buffer, fenceFd); | 
 | } | 
 |  | 
 | int ReliableSurface::hook_perform(ANativeWindow* window, ANativeWindow_performFn perform, | 
 |                                   void* data, int operation, va_list args) { | 
 |     // Drop the reserved buffer if there is one since this (probably) mutated buffer dimensions | 
 |     // TODO: Filter to things that only affect the reserved buffer | 
 |     // TODO: Can we mutate the reserved buffer in some cases? | 
 |     ReliableSurface* rs = reinterpret_cast<ReliableSurface*>(data); | 
 |     rs->clearReservedBuffer(); | 
 |  | 
 |     va_list argsCopy; | 
 |     va_copy(argsCopy, args); | 
 |     int result = perform(window, operation, argsCopy); | 
 |  | 
 |     { | 
 |         std::lock_guard _lock{rs->mMutex}; | 
 |  | 
 |         switch (operation) { | 
 |             case ANATIVEWINDOW_PERFORM_SET_USAGE: | 
 |                 rs->mUsage = va_arg(args, uint32_t); | 
 |                 break; | 
 |             case ANATIVEWINDOW_PERFORM_SET_USAGE64: | 
 |                 rs->mUsage = va_arg(args, uint64_t); | 
 |                 break; | 
 |             case ANATIVEWINDOW_PERFORM_SET_BUFFERS_GEOMETRY: | 
 |                 /* width */ va_arg(args, uint32_t); | 
 |                 /* height */ va_arg(args, uint32_t); | 
 |                 rs->mFormat = static_cast<AHardwareBuffer_Format>(va_arg(args, int32_t)); | 
 |                 break; | 
 |             case ANATIVEWINDOW_PERFORM_SET_BUFFERS_FORMAT: | 
 |                 rs->mFormat = static_cast<AHardwareBuffer_Format>(va_arg(args, int32_t)); | 
 |                 break; | 
 |             case NATIVE_WINDOW_SET_BUFFER_COUNT: | 
 |                 size_t bufferCount = va_arg(args, size_t); | 
 |                 if (bufferCount >= rs->mExpectedBufferCount) { | 
 |                     rs->mDidSetExtraBuffers = true; | 
 |                 } else { | 
 |                     ALOGD("HOOK FAILED! Expected %zd got = %zd", rs->mExpectedBufferCount, bufferCount); | 
 |                 } | 
 |                 break; | 
 |         } | 
 |     } | 
 |     return result; | 
 | } | 
 |  | 
 | int ReliableSurface::hook_query(const ANativeWindow *window, ANativeWindow_queryFn query, | 
 |         void *data, int what, int *value) { | 
 |     ReliableSurface* rs = reinterpret_cast<ReliableSurface*>(data); | 
 |     int result = query(window, what, value); | 
 |     if (what == ANATIVEWINDOW_QUERY_MIN_UNDEQUEUED_BUFFERS && result == OK) { | 
 |         std::lock_guard _lock{rs->mMutex}; | 
 |         rs->mExpectedBufferCount = *value + 2; | 
 |     } | 
 |     return result; | 
 | } | 
 |  | 
 | };  // namespace android::uirenderer::renderthread |