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

Commit 37934a22 authored by Ytai Ben-Tsvi's avatar Ytai Ben-Tsvi
Browse files

Add a standardized shared memory type

This adds a simple SharedFileRegion type that can be used to pass blocks
of shared memory between processes in a structured AIDL interface.

In addition a small utility library is provided for interfacing with
existing code that uses IMemory.

The intention is to add utilities for direct usage of the memory
content.

Bug: 160253486
Test: Ran the included unit test. Integration testing as part of whole
      topic tests via the audio effect tests.
Change-Id: I3f923047e364ba44fda316bdd563561ebec201b2
parent 4eb89959
Loading
Loading
Loading
Loading
+50 −0
Original line number Diff line number Diff line
aidl_interface {
    name: "shared-file-region-aidl",
    unstable: true,
    local_include_dir: "aidl",
    srcs: [
        "aidl/android/media/SharedFileRegion.aidl",
    ],
}

cc_library {
    name: "libshmemcompat",
    export_include_dirs: ["include"],
    srcs: ["ShmemCompat.cpp"],
    shared_libs: [
        "libbinder",
        "libshmemutil",
        "libutils",
        "shared-file-region-aidl-cpp",
    ],
    export_shared_lib_headers: [
        "libbinder",
        "libutils",
        "shared-file-region-aidl-cpp",
    ],
}

cc_library {
    name: "libshmemutil",
    export_include_dirs: ["include"],
    srcs: ["ShmemUtil.cpp"],
    shared_libs: [
        "shared-file-region-aidl-cpp",
    ],
    export_shared_lib_headers: [
        "shared-file-region-aidl-cpp",
    ],
}

cc_test {
    name: "shmemTest",
    srcs: ["ShmemTest.cpp"],
    shared_libs: [
        "libbinder",
        "libshmemcompat",
        "libshmemutil",
        "libutils",
        "shared-file-region-aidl-cpp",
    ],
    test_suites: ["device-tests"],
}

media/libshmem/OWNERS

0 → 100644
+3 −0
Original line number Diff line number Diff line
ytai@google.com
mnaganov@google.com
elaurent@google.com
+6 −0
Original line number Diff line number Diff line
# libshmem

This library provides facilities for sharing memory across processes over (stable) AIDL. The main
feature is the definition of the `android.media.SharedMemory` AIDL type, which represents a block of
memory that can be shared between processes. In addition, a few utilities are provided to facilitate
the use of shared memory and to integrate with legacy code that uses older facilities.
 No newline at end of file
+98 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2020 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 "media/ShmemCompat.h"

#include "binder/MemoryBase.h"
#include "binder/MemoryHeapBase.h"
#include "media/ShmemUtil.h"

namespace android {
namespace media {

bool convertSharedFileRegionToIMemory(const SharedFileRegion& shmem,
                                      sp<IMemory>* result) {
    if (!validateSharedFileRegion(shmem)) {
        return false;
    }

    if (shmem.fd.get() < 0) {
        *result = nullptr;
        return true;
    }

    // Heap offset and size must be page aligned.
    const size_t pageSize = getpagesize();
    const size_t pageMask = ~(pageSize - 1);

    // OK if this wraps.
    const uint64_t endOffset = static_cast<uint64_t>(shmem.offset) +
            static_cast<uint64_t>(shmem.size);

    // Round down to page boundary.
    const uint64_t heapStartOffset = shmem.offset & pageMask;
    // Round up to page boundary.
    const uint64_t heapEndOffset = (endOffset + pageSize - 1) & pageMask;
    const uint64_t heapSize = heapEndOffset - heapStartOffset;

    if (heapStartOffset > std::numeric_limits<size_t>::max() ||
        heapSize > std::numeric_limits<size_t>::max()) {
        return false;
    }

    const sp<MemoryHeapBase> heap =
            new MemoryHeapBase(shmem.fd.get(), heapSize, 0, heapStartOffset);
    *result = sp<MemoryBase>::make(heap,
                                   shmem.offset - heapStartOffset,
                                   shmem.size);
    return true;
}

bool convertIMemoryToSharedFileRegion(const sp<IMemory>& mem,
                                      SharedFileRegion* result) {
    *result = SharedFileRegion();
    if (mem == nullptr) {
        return true;
    }

    ssize_t offset;
    size_t size;

    sp<IMemoryHeap> heap = mem->getMemory(&offset, &size);
    if (heap != nullptr) {
        // Make sure the offset and size do not overflow from int64 boundaries.
        if (size > std::numeric_limits<int64_t>::max() ||
                offset > std::numeric_limits<int64_t>::max() ||
                heap->getOffset() > std::numeric_limits<int64_t>::max() ||
                static_cast<uint64_t>(heap->getOffset()) +
                static_cast<uint64_t>(offset)
                        > std::numeric_limits<int64_t>::max()) {
            return false;
        }

        const int fd = fcntl(heap->getHeapID(), F_DUPFD_CLOEXEC, 0);
        if (fd < 0) {
            return false;
        }
        result->fd.reset(base::unique_fd(fd));
        result->size = size;
        result->offset = heap->getOffset() + offset;
    }

    return true;
}

}  // namespace media
}  // namespace android
+87 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2020 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 <gtest/gtest.h>

#include "binder/MemoryBase.h"
#include "binder/MemoryHeapBase.h"
#include "media/ShmemCompat.h"
#include "media/ShmemUtil.h"

namespace android {
namespace media {
namespace {

// Creates a SharedFileRegion instance with a null FD.
SharedFileRegion makeSharedFileRegion(int64_t offset, int64_t size) {
    SharedFileRegion shmem;
    shmem.offset = offset;
    shmem.size = size;
    return shmem;
}

sp<IMemory> makeIMemory(const std::vector<uint8_t>& content) {
    constexpr size_t kOffset = 19;

    sp<MemoryHeapBase> heap = new MemoryHeapBase(content.size());
    sp<IMemory> result = sp<MemoryBase>::make(heap, kOffset, content.size());
    memcpy(result->unsecurePointer(), content.data(), content.size());
    return result;
}

TEST(ShmemTest, Validate) {
    EXPECT_TRUE(validateSharedFileRegion(makeSharedFileRegion(0, 0)));
    EXPECT_TRUE(validateSharedFileRegion(makeSharedFileRegion(1, 2)));
    EXPECT_FALSE(validateSharedFileRegion(makeSharedFileRegion(-1, 2)));
    EXPECT_FALSE(validateSharedFileRegion(makeSharedFileRegion(2, -1)));
    EXPECT_TRUE(validateSharedFileRegion(makeSharedFileRegion(
            std::numeric_limits<int64_t>::max(),
            std::numeric_limits<int64_t>::max())));
}

TEST(ShmemTest, Conversion) {
    sp<IMemory> reconstructed;
    {
        SharedFileRegion shmem;
        sp<IMemory> imem = makeIMemory({6, 5, 3});
        ASSERT_TRUE(convertIMemoryToSharedFileRegion(imem, &shmem));
        ASSERT_EQ(3, shmem.size);
        ASSERT_GE(shmem.fd.get(), 0);
        ASSERT_TRUE(convertSharedFileRegionToIMemory(shmem, &reconstructed));
    }
    ASSERT_EQ(3, reconstructed->size());
    const uint8_t* p =
            reinterpret_cast<const uint8_t*>(reconstructed->unsecurePointer());
    EXPECT_EQ(6, p[0]);
    EXPECT_EQ(5, p[1]);
    EXPECT_EQ(3, p[2]);
}

TEST(ShmemTest, NullConversion) {
    sp<IMemory> reconstructed;
    {
        SharedFileRegion shmem;
        sp<IMemory> imem;
        ASSERT_TRUE(convertIMemoryToSharedFileRegion(imem, &shmem));
        ASSERT_EQ(0, shmem.size);
        ASSERT_LT(shmem.fd.get(), 0);
        ASSERT_TRUE(convertSharedFileRegionToIMemory(shmem, &reconstructed));
    }
    ASSERT_EQ(nullptr, reconstructed);
}

}  // namespace
}  // namespace media
}  // namespace android
Loading