Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 2c5a7c4f authored by Patrick Williams's avatar Patrick Williams Committed by Android (Google) Code Review
Browse files

Merge "Add BufferReleaseChannel" into main

parents 93787906 f693bcf2
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -255,6 +255,7 @@ filegroup {
        "BitTube.cpp",
        "BLASTBufferQueue.cpp",
        "BufferItemConsumer.cpp",
        "BufferReleaseChannel.cpp",
        "Choreographer.cpp",
        "CompositorTiming.cpp",
        "ConsumerBase.cpp",
+358 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2024 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.
 */

#define LOG_TAG "BufferReleaseChannel"

#include <fcntl.h>
#include <sys/socket.h>
#include <sys/uio.h>

#include <android-base/result.h>
#include <android/binder_status.h>
#include <binder/Parcel.h>
#include <utils/Flattenable.h>

#include <gui/BufferReleaseChannel.h>
#include <private/gui/ParcelUtils.h>

using android::base::Result;

namespace android::gui {

namespace {

template <typename T>
static void readAligned(const void*& buffer, size_t& size, T& value) {
    size -= FlattenableUtils::align<alignof(T)>(buffer);
    FlattenableUtils::read(buffer, size, value);
}

template <typename T>
static void writeAligned(void*& buffer, size_t& size, T value) {
    size -= FlattenableUtils::align<alignof(T)>(buffer);
    FlattenableUtils::write(buffer, size, value);
}

template <typename T>
static void addAligned(size_t& size, T /* value */) {
    size = FlattenableUtils::align<sizeof(T)>(size);
    size += sizeof(T);
}

template <typename T>
static inline constexpr uint32_t low32(const T n) {
    return static_cast<uint32_t>(static_cast<uint64_t>(n));
}

template <typename T>
static inline constexpr uint32_t high32(const T n) {
    return static_cast<uint32_t>(static_cast<uint64_t>(n) >> 32);
}

template <typename T>
static inline constexpr T to64(const uint32_t lo, const uint32_t hi) {
    return static_cast<T>(static_cast<uint64_t>(hi) << 32 | lo);
}

} // namespace

size_t BufferReleaseChannel::Message::getPodSize() const {
    size_t size = 0;
    addAligned(size, low32(releaseCallbackId.bufferId));
    addAligned(size, high32(releaseCallbackId.bufferId));
    addAligned(size, low32(releaseCallbackId.framenumber));
    addAligned(size, high32(releaseCallbackId.framenumber));
    addAligned(size, maxAcquiredBufferCount);
    return size;
}

size_t BufferReleaseChannel::Message::getFlattenedSize() const {
    size_t size = releaseFence->getFlattenedSize();
    size = FlattenableUtils::align<4>(size);
    size += getPodSize();
    return size;
}

status_t BufferReleaseChannel::Message::flatten(void*& buffer, size_t& size, int*& fds,
                                                size_t& count) const {
    if (status_t err = releaseFence->flatten(buffer, size, fds, count); err != OK) {
        return err;
    }
    size -= FlattenableUtils::align<4>(buffer);

    // Check we still have enough space
    if (size < getPodSize()) {
        return NO_MEMORY;
    }

    writeAligned(buffer, size, low32(releaseCallbackId.bufferId));
    writeAligned(buffer, size, high32(releaseCallbackId.bufferId));
    writeAligned(buffer, size, low32(releaseCallbackId.framenumber));
    writeAligned(buffer, size, high32(releaseCallbackId.framenumber));
    writeAligned(buffer, size, maxAcquiredBufferCount);
    return OK;
}

status_t BufferReleaseChannel::Message::unflatten(void const*& buffer, size_t& size,
                                                  int const*& fds, size_t& count) {
    releaseFence = new Fence();
    if (status_t err = releaseFence->unflatten(buffer, size, fds, count); err != OK) {
        return err;
    }
    size -= FlattenableUtils::align<4>(buffer);

    // Check we still have enough space
    if (size < getPodSize()) {
        return OK;
    }

    uint32_t bufferIdLo = 0, bufferIdHi = 0;
    uint32_t frameNumberLo = 0, frameNumberHi = 0;

    readAligned(buffer, size, bufferIdLo);
    readAligned(buffer, size, bufferIdHi);
    releaseCallbackId.bufferId = to64<int64_t>(bufferIdLo, bufferIdHi);
    readAligned(buffer, size, frameNumberLo);
    readAligned(buffer, size, frameNumberHi);
    releaseCallbackId.framenumber = to64<uint64_t>(frameNumberLo, frameNumberHi);
    readAligned(buffer, size, maxAcquiredBufferCount);

    return OK;
}

status_t BufferReleaseChannel::ConsumerEndpoint::readReleaseFence(
        ReleaseCallbackId& outReleaseCallbackId, sp<Fence>& outReleaseFence,
        uint32_t& outMaxAcquiredBufferCount) {
    Message message;
    mFlattenedBuffer.resize(message.getFlattenedSize());
    std::array<uint8_t, CMSG_SPACE(sizeof(int))> controlMessageBuffer;

    iovec iov{
            .iov_base = mFlattenedBuffer.data(),
            .iov_len = mFlattenedBuffer.size(),
    };

    msghdr msg{
            .msg_iov = &iov,
            .msg_iovlen = 1,
            .msg_control = controlMessageBuffer.data(),
            .msg_controllen = controlMessageBuffer.size(),
    };

    int result;
    do {
        result = recvmsg(mFd, &msg, 0);
    } while (result == -1 && errno == EINTR);
    if (result == -1) {
        if (errno == EWOULDBLOCK || errno == EAGAIN) {
            return WOULD_BLOCK;
        }
        ALOGE("Error reading release fence from socket: error %#x (%s)", errno, strerror(errno));
        return UNKNOWN_ERROR;
    }

    if (msg.msg_iovlen != 1) {
        ALOGE("Error reading release fence from socket: bad data length");
        return UNKNOWN_ERROR;
    }

    if (msg.msg_controllen % sizeof(int) != 0) {
        ALOGE("Error reading release fence from socket: bad fd length");
        return UNKNOWN_ERROR;
    }

    size_t dataLen = msg.msg_iov->iov_len;
    const void* data = static_cast<const void*>(msg.msg_iov->iov_base);
    if (!data) {
        ALOGE("Error reading release fence from socket: no buffer data");
        return UNKNOWN_ERROR;
    }

    size_t fdCount = 0;
    const int* fdData = nullptr;
    if (cmsghdr* cmsg = CMSG_FIRSTHDR(&msg)) {
        fdData = reinterpret_cast<const int*>(CMSG_DATA(cmsg));
        fdCount = (cmsg->cmsg_len - CMSG_LEN(0)) / sizeof(int);
    }

    if (status_t err = message.unflatten(data, dataLen, fdData, fdCount); err != OK) {
        return err;
    }

    outReleaseCallbackId = message.releaseCallbackId;
    outReleaseFence = std::move(message.releaseFence);
    outMaxAcquiredBufferCount = message.maxAcquiredBufferCount;

    return OK;
}

int BufferReleaseChannel::ProducerEndpoint::writeReleaseFence(const ReleaseCallbackId& callbackId,
                                                              const sp<Fence>& fence,
                                                              uint32_t maxAcquiredBufferCount) {
    Message message{callbackId, fence ? fence : Fence::NO_FENCE, maxAcquiredBufferCount};
    mFlattenedBuffer.resize(message.getFlattenedSize());
    int flattenedFd;
    {
        // Make copies of needed items since flatten modifies them, and we don't
        // want to send anything if there's an error during flatten.
        void* flattenedBufferPtr = mFlattenedBuffer.data();
        size_t flattenedBufferSize = mFlattenedBuffer.size();
        int* flattenedFdPtr = &flattenedFd;
        size_t flattenedFdCount = 1;
        if (status_t err = message.flatten(flattenedBufferPtr, flattenedBufferSize, flattenedFdPtr,
                                           flattenedFdCount);
            err != OK) {
            ALOGE("Failed to flatten BufferReleaseChannel message.");
            return err;
        }
    }

    iovec iov{
            .iov_base = mFlattenedBuffer.data(),
            .iov_len = mFlattenedBuffer.size(),
    };

    msghdr msg{
            .msg_iov = &iov,
            .msg_iovlen = 1,
    };

    std::array<uint8_t, CMSG_SPACE(sizeof(int))> controlMessageBuffer;
    if (fence && fence->isValid()) {
        msg.msg_control = controlMessageBuffer.data();
        msg.msg_controllen = controlMessageBuffer.size();

        cmsghdr* cmsg = CMSG_FIRSTHDR(&msg);
        cmsg->cmsg_level = SOL_SOCKET;
        cmsg->cmsg_type = SCM_RIGHTS;
        cmsg->cmsg_len = CMSG_LEN(sizeof(int));
        memcpy(CMSG_DATA(cmsg), &flattenedFd, sizeof(int));
    }

    int result;
    do {
        result = sendmsg(mFd, &msg, 0);
    } while (result == -1 && errno == EINTR);
    if (result == -1) {
        ALOGD("Error writing release fence to socket: error %#x (%s)", errno, strerror(errno));
        return -errno;
    }

    return OK;
}

status_t BufferReleaseChannel::ProducerEndpoint::readFromParcel(const android::Parcel* parcel) {
    if (!parcel) return STATUS_BAD_VALUE;
    SAFE_PARCEL(parcel->readUtf8FromUtf16, &mName);
    SAFE_PARCEL(parcel->readUniqueFileDescriptor, &mFd);
    return STATUS_OK;
}

status_t BufferReleaseChannel::ProducerEndpoint::writeToParcel(android::Parcel* parcel) const {
    if (!parcel) return STATUS_BAD_VALUE;
    SAFE_PARCEL(parcel->writeUtf8AsUtf16, mName);
    SAFE_PARCEL(parcel->writeUniqueFileDescriptor, mFd);
    return STATUS_OK;
}

status_t BufferReleaseChannel::open(std::string name,
                                    std::unique_ptr<ConsumerEndpoint>& outConsumer,
                                    std::shared_ptr<ProducerEndpoint>& outProducer) {
    outConsumer.reset();
    outProducer.reset();

    int sockets[2];
    if (socketpair(AF_UNIX, SOCK_SEQPACKET, 0, sockets)) {
        ALOGE("[%s] Failed to create socket pair. errorno=%d message='%s'", name.c_str(), errno,
              strerror(errno));
        return -errno;
    }

    android::base::unique_fd consumerFd(sockets[0]);
    android::base::unique_fd producerFd(sockets[1]);

    // Socket buffer size. The default is typically about 128KB, which is much larger than
    // we really need.
    size_t bufferSize = 32 * 1024;
    if (setsockopt(consumerFd.get(), SOL_SOCKET, SO_SNDBUF, &bufferSize, sizeof(bufferSize)) ==
        -1) {
        ALOGE("[%s] Failed to set consumer socket send buffer size. errno=%d message='%s'",
              name.c_str(), errno, strerror(errno));
        return -errno;
    }
    if (setsockopt(consumerFd.get(), SOL_SOCKET, SO_RCVBUF, &bufferSize, sizeof(bufferSize)) ==
        -1) {
        ALOGE("[%s] Failed to set consumer socket receive buffer size. errno=%d "
              "message='%s'",
              name.c_str(), errno, strerror(errno));
        return -errno;
    }
    if (setsockopt(producerFd.get(), SOL_SOCKET, SO_SNDBUF, &bufferSize, sizeof(bufferSize)) ==
        -1) {
        ALOGE("[%s] Failed to set producer socket send buffer size. errno=%d message='%s'",
              name.c_str(), errno, strerror(errno));
        return -errno;
    }
    if (setsockopt(producerFd.get(), SOL_SOCKET, SO_RCVBUF, &bufferSize, sizeof(bufferSize)) ==
        -1) {
        ALOGE("[%s] Failed to set producer socket receive buffer size. errno=%d "
              "message='%s'",
              name.c_str(), errno, strerror(errno));
        return -errno;
    }

    // Configure the consumer socket to be non-blocking.
    int flags = fcntl(consumerFd.get(), F_GETFL, 0);
    if (flags == -1) {
        ALOGE("[%s] Failed to get consumer socket flags. errno=%d message='%s'", name.c_str(),
              errno, strerror(errno));
        return -errno;
    }
    if (fcntl(consumerFd.get(), F_SETFL, flags | O_NONBLOCK) == -1) {
        ALOGE("[%s] Failed to set consumer socket to non-blocking mode. errno=%d "
              "message='%s'",
              name.c_str(), errno, strerror(errno));
        return -errno;
    }

    // Configure a timeout for the producer socket.
    const timeval timeout{.tv_sec = 1, .tv_usec = 0};
    if (setsockopt(producerFd.get(), SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof(timeval)) == -1) {
        ALOGE("[%s] Failed to set producer socket timeout. errno=%d message='%s'", name.c_str(),
              errno, strerror(errno));
        return -errno;
    }

    // Make the consumer read-only
    if (shutdown(consumerFd.get(), SHUT_WR) == -1) {
        ALOGE("[%s] Failed to shutdown writing on consumer socket. errno=%d message='%s'",
              name.c_str(), errno, strerror(errno));
        return -errno;
    }

    // Make the producer write-only
    if (shutdown(producerFd.get(), SHUT_RD) == -1) {
        ALOGE("[%s] Failed to shutdown reading on producer socket. errno=%d message='%s'",
              name.c_str(), errno, strerror(errno));
        return -errno;
    }

    outConsumer = std::make_unique<ConsumerEndpoint>(name, std::move(consumerFd));
    outProducer = std::make_shared<ProducerEndpoint>(std::move(name), std::move(producerFd));
    return STATUS_OK;
}

} // namespace android::gui
 No newline at end of file
+19 −1
Original line number Diff line number Diff line
@@ -17,7 +17,6 @@
#define LOG_TAG "LayerState"

#include <cinttypes>
#include <cmath>

#include <android/gui/ISurfaceComposerClient.h>
#include <android/native_window.h>
@@ -194,6 +193,13 @@ status_t layer_state_t::write(Parcel& output) const
    SAFE_PARCEL(output.writeFloat, currentHdrSdrRatio);
    SAFE_PARCEL(output.writeFloat, desiredHdrSdrRatio);
    SAFE_PARCEL(output.writeInt32, static_cast<int32_t>(cachingHint));

    const bool hasBufferReleaseChannel = (bufferReleaseChannel != nullptr);
    SAFE_PARCEL(output.writeBool, hasBufferReleaseChannel);
    if (hasBufferReleaseChannel) {
        SAFE_PARCEL(output.writeParcelable, *bufferReleaseChannel);
    }

    return NO_ERROR;
}

@@ -339,6 +345,13 @@ status_t layer_state_t::read(const Parcel& input)
    SAFE_PARCEL(input.readInt32, &tmpInt32);
    cachingHint = static_cast<gui::CachingHint>(tmpInt32);

    bool hasBufferReleaseChannel;
    SAFE_PARCEL(input.readBool, &hasBufferReleaseChannel);
    if (hasBufferReleaseChannel) {
        bufferReleaseChannel = std::make_shared<gui::BufferReleaseChannel::ProducerEndpoint>();
        SAFE_PARCEL(input.readParcelable, bufferReleaseChannel.get());
    }

    return NO_ERROR;
}

@@ -718,6 +731,10 @@ void layer_state_t::merge(const layer_state_t& other) {
    if (other.what & eFlushJankData) {
        what |= eFlushJankData;
    }
    if (other.what & eBufferReleaseChannelChanged) {
        what |= eBufferReleaseChannelChanged;
        bufferReleaseChannel = other.bufferReleaseChannel;
    }
    if ((other.what & what) != other.what) {
        ALOGE("Unmerged SurfaceComposer Transaction properties. LayerState::merge needs updating? "
              "other.what=0x%" PRIX64 " what=0x%" PRIX64 " unmerged flags=0x%" PRIX64,
@@ -797,6 +814,7 @@ uint64_t layer_state_t::diff(const layer_state_t& other) const {
    CHECK_DIFF(diff, eColorChanged, other, color.rgb);
    CHECK_DIFF(diff, eColorSpaceAgnosticChanged, other, colorSpaceAgnostic);
    CHECK_DIFF(diff, eDimmingEnabledChanged, other, dimmingEnabled);
    if (other.what & eBufferReleaseChannelChanged) diff |= eBufferReleaseChannelChanged;
    return diff;
}

+16 −0
Original line number Diff line number Diff line
@@ -2390,6 +2390,22 @@ SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setDropI
    return *this;
}

SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setBufferReleaseChannel(
        const sp<SurfaceControl>& sc,
        const std::shared_ptr<gui::BufferReleaseChannel::ProducerEndpoint>& channel) {
    layer_state_t* s = getLayerState(sc);
    if (!s) {
        mStatus = BAD_INDEX;
        return *this;
    }

    s->what |= layer_state_t::eBufferReleaseChannelChanged;
    s->bufferReleaseChannel = channel;

    registerSurfaceControlForCallback(sc);
    return *this;
}

// ---------------------------------------------------------------------------

DisplayState& SurfaceComposerClient::Transaction::getDisplayState(const sp<IBinder>& token) {
+125 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2024 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.
 */

#pragma once

#include <string>
#include <vector>

#include <android-base/unique_fd.h>

#include <binder/Parcelable.h>
#include <gui/ITransactionCompletedListener.h>
#include <ui/Fence.h>
#include <utils/Errors.h>

namespace android::gui {

/**
 * IPC wrapper to pass release fences from SurfaceFlinger to apps via a local unix domain socket.
 */
class BufferReleaseChannel {
private:
    class Endpoint {
    public:
        Endpoint(std::string name, android::base::unique_fd fd)
              : mName(std::move(name)), mFd(std::move(fd)) {}
        Endpoint() {}

        Endpoint(Endpoint&&) noexcept = default;
        Endpoint& operator=(Endpoint&&) noexcept = default;

        Endpoint(const Endpoint&) = delete;
        void operator=(const Endpoint&) = delete;

        const android::base::unique_fd& getFd() const { return mFd; }

    protected:
        std::string mName;
        android::base::unique_fd mFd;
    };

public:
    class ConsumerEndpoint : public Endpoint {
    public:
        ConsumerEndpoint(std::string name, android::base::unique_fd fd)
              : Endpoint(std::move(name), std::move(fd)) {}

        /**
         * Reads a release fence from the BufferReleaseChannel.
         *
         * Returns OK on success.
         * Returns WOULD_BLOCK if there is no fence present.
         * Other errors probably indicate that the channel is broken.
         */
        status_t readReleaseFence(ReleaseCallbackId& outReleaseCallbackId,
                                  sp<Fence>& outReleaseFence, uint32_t& maxAcquiredBufferCount);

    private:
        std::vector<uint8_t> mFlattenedBuffer;
    };

    class ProducerEndpoint : public Endpoint, public Parcelable {
    public:
        ProducerEndpoint(std::string name, android::base::unique_fd fd)
              : Endpoint(std::move(name), std::move(fd)) {}
        ProducerEndpoint() {}

        status_t readFromParcel(const android::Parcel* parcel) override;
        status_t writeToParcel(android::Parcel* parcel) const override;

        status_t writeReleaseFence(const ReleaseCallbackId&, const sp<Fence>& releaseFence,
                                   uint32_t maxAcquiredBufferCount);

    private:
        std::vector<uint8_t> mFlattenedBuffer;
    };

    /**
     * Create two endpoints that make up the BufferReleaseChannel.
     *
     * Return OK on success.
     */
    static status_t open(const std::string name, std::unique_ptr<ConsumerEndpoint>& outConsumer,
                         std::shared_ptr<ProducerEndpoint>& outProducer);

    struct Message : public Flattenable<Message> {
        ReleaseCallbackId releaseCallbackId;
        sp<Fence> releaseFence = Fence::NO_FENCE;
        uint32_t maxAcquiredBufferCount;

        Message() = default;
        Message(ReleaseCallbackId releaseCallbackId, sp<Fence> releaseFence,
                uint32_t maxAcquiredBufferCount)
              : releaseCallbackId{releaseCallbackId},
                releaseFence{std::move(releaseFence)},
                maxAcquiredBufferCount{maxAcquiredBufferCount} {}

        // Flattenable protocol
        size_t getFlattenedSize() const;

        size_t getFdCount() const { return releaseFence->getFdCount(); }

        status_t flatten(void*& buffer, size_t& size, int*& fds, size_t& count) const;

        status_t unflatten(void const*& buffer, size_t& size, int const*& fds, size_t& count);

    private:
        size_t getPodSize() const;
    };
};

} // namespace android::gui
Loading