1 /*
2  * Copyright (C) 2023 Huawei Device Co., Ltd.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at
6  *
7  *     http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15 
16 #include "av_shared_memory_ext.h"
17 #include <unistd.h>
18 #include "ashmem.h"
19 #include "av_shared_allocator.h"
20 #include "avbuffer_utils.h"
21 #include "buffer/avallocator.h"
22 #include "common/log.h"
23 #include "common/status.h"
24 #include "message_parcel.h"
25 #include "scope_guard.h"
26 
27 #ifdef MEDIA_OHOS
28 #include "sys/mman.h"
29 #endif
30 
31 namespace {
32 constexpr OHOS::HiviewDFX::HiLogLabel LABEL = { LOG_CORE, LOG_DOMAIN_FOUNDATION, "AVSharedMemoryExt" };
33 }
34 
35 namespace OHOS {
36 namespace Media {
CreateSharedAllocator(MemoryFlag memFlag)37 std::shared_ptr<AVAllocator> AVAllocatorFactory::CreateSharedAllocator(MemoryFlag memFlag)
38 {
39     auto allocator = std::shared_ptr<AVSharedAllocator>(new AVSharedAllocator());
40     FALSE_RETURN_V_MSG_E(allocator != nullptr, nullptr, "Create AVSharedAllocator failed, no memory");
41     allocator->memFlag_ = memFlag;
42     return allocator;
43 }
44 
AVSharedAllocator()45 AVSharedAllocator::AVSharedAllocator(){};
46 
Alloc(int32_t capacity)47 void *AVSharedAllocator::Alloc(int32_t capacity)
48 {
49     int32_t fd = AshmemCreate(0, static_cast<size_t>(capacity)); // release by close(fd)
50     FALSE_RETURN_V_MSG_E(fd > 0, nullptr, "fd is invalid, fd:%{public}d", fd);
51 
52     return reinterpret_cast<void *>(fd);
53 }
54 
Free(void * ptr)55 bool AVSharedAllocator::Free(void *ptr)
56 {
57     int32_t fd = reinterpret_cast<intptr_t>(ptr);
58     if (fd > 0) {
59         (void)::close(fd);
60         return true;
61     }
62     return false;
63 }
64 
GetMemoryType()65 MemoryType AVSharedAllocator::GetMemoryType()
66 {
67     return MemoryType::SHARED_MEMORY;
68 }
69 
GetMemFlag()70 MemoryFlag AVSharedAllocator::GetMemFlag()
71 {
72     return memFlag_;
73 }
74 
AVSharedMemoryExt()75 AVSharedMemoryExt::AVSharedMemoryExt() : fd_(-1), isFirstFlag_(true), memFlag_(MemoryFlag::MEMORY_READ_ONLY) {}
76 
~AVSharedMemoryExt()77 AVSharedMemoryExt::~AVSharedMemoryExt()
78 {
79     UnMapMemoryAddr();
80     if (allocator_ == nullptr) {
81         if (fd_ > 0) {
82             (void)::close(fd_);
83             fd_ = -1;
84         }
85         return;
86     }
87     bool ret = allocator_->Free(reinterpret_cast<void *>(fd_));
88     FALSE_RETURN_MSG(ret, "Free memory failed, instance: 0x%{public}06" PRIXPTR, FAKE_POINTER(this));
89 }
90 
Init()91 Status AVSharedMemoryExt::Init()
92 {
93     memFlag_ = std::static_pointer_cast<AVSharedAllocator>(allocator_)->GetMemFlag();
94 
95     int32_t allocSize = align_ ? (capacity_ + align_ - 1) : capacity_;
96     fd_ = reinterpret_cast<intptr_t>(allocator_->Alloc(allocSize));
97     FALSE_RETURN_V_MSG_E(fd_ > 0, Status::ERROR_NO_MEMORY, "Alloc AVSharedMemoryExt failed");
98 
99     uintptr_t addrBase = reinterpret_cast<uintptr_t>(base_);
100     offset_ = static_cast<int32_t>(AlignUp(addrBase, static_cast<uintptr_t>(offset_)) - addrBase);
101 
102     return Status::OK;
103 }
104 
Init(MessageParcel & parcel)105 Status AVSharedMemoryExt::Init(MessageParcel &parcel)
106 {
107 #ifdef MEDIA_OHOS
108     fd_ = parcel.ReadFileDescriptor();
109     FALSE_RETURN_V_MSG_E(fd_ > 0, Status::ERROR_INVALID_DATA, "File descriptor is invalid");
110     memFlag_ = static_cast<MemoryFlag>(parcel.ReadUint32());
111     return Status::OK;
112 #else
113     return Status::OK;
114 #endif
115 }
116 
WriteToMessageParcel(MessageParcel & parcel)117 bool AVSharedMemoryExt::WriteToMessageParcel(MessageParcel &parcel)
118 {
119 #ifdef MEDIA_OHOS
120     MessageParcel bufferParcel;
121     bool ret = bufferParcel.WriteFileDescriptor(fd_) && bufferParcel.WriteUint32(static_cast<uint32_t>(memFlag_));
122     if (ret) {
123         parcel.Append(bufferParcel);
124     }
125     return ret;
126 #else
127     return false;
128 #endif
129 }
130 
ReadFromMessageParcel(MessageParcel & parcel)131 bool AVSharedMemoryExt::ReadFromMessageParcel(MessageParcel &parcel)
132 {
133 #ifdef MEDIA_OHOS
134     int32_t fd = parcel.ReadFileDescriptor();
135     (void)parcel.ReadUint32();
136     if (fd > 0) {
137         (void)::close(fd);
138     }
139 #endif
140     return true;
141 }
142 
GetAddr()143 uint8_t *AVSharedMemoryExt::GetAddr()
144 {
145     if (isFirstFlag_) {
146         Status ret = MapMemoryAddr();
147         FALSE_RETURN_V_MSG_E(ret == Status::OK, nullptr, "MapMemory failed");
148         isFirstFlag_ = false;
149     }
150     return base_;
151 }
152 
GetMemoryType()153 MemoryType AVSharedMemoryExt::GetMemoryType()
154 {
155     return MemoryType::SHARED_MEMORY;
156 }
157 
GetMemoryFlag()158 MemoryFlag AVSharedMemoryExt::GetMemoryFlag()
159 {
160     return memFlag_;
161 }
162 
GetFileDescriptor()163 int32_t AVSharedMemoryExt::GetFileDescriptor()
164 {
165     return fd_;
166 }
167 
UnMapMemoryAddr()168 void AVSharedMemoryExt::UnMapMemoryAddr() noexcept
169 {
170 #ifdef MEDIA_OHOS
171     if (base_ != nullptr) {
172         (void)::munmap(base_, static_cast<size_t>(capacity_));
173         base_ = nullptr;
174         size_ = 0;
175     }
176 #endif
177 }
178 
MapMemoryAddr()179 Status AVSharedMemoryExt::MapMemoryAddr()
180 {
181 #ifdef MEDIA_OHOS
182     ON_SCOPE_EXIT(0)
183     {
184         MEDIA_LOG_E("create avsharedmemory failed. "
185                     "uid:" PUBLIC_LOG_U64 ", size:%{public}d, flags:0x%{public}x, fd:%{public}d",
186                     uid_, capacity_, memFlag_, fd_);
187         UnMapMemoryAddr();
188         return Status::ERROR_NO_MEMORY;
189     };
190     FALSE_RETURN_V_MSG_E(capacity_ > 0, Status::ERROR_INVALID_DATA, "size is invalid, size:%{public}d", capacity_);
191     unsigned int prot = PROT_READ | PROT_WRITE;
192     if (memFlag_ == MemoryFlag::MEMORY_READ_ONLY) {
193         prot &= ~PROT_WRITE;
194     } else if (memFlag_ == MemoryFlag::MEMORY_WRITE_ONLY) {
195         prot &= ~PROT_READ;
196     }
197     int result = AshmemSetProt(fd_, static_cast<int>(prot));
198     FALSE_RETURN_V_MSG_E(result >= 0, Status::ERROR_INVALID_OPERATION, "AshmemSetProt failed, result:%{public}d",
199                          result);
200 
201     void *addr = ::mmap(nullptr, static_cast<size_t>(capacity_), static_cast<int>(prot), MAP_SHARED, fd_, 0);
202     FALSE_RETURN_V_MSG_E(addr != MAP_FAILED, Status::ERROR_INVALID_OPERATION, "mmap failed, please check params");
203 
204     base_ = static_cast<uint8_t *>(addr);
205     CANCEL_SCOPE_EXIT_GUARD(0);
206 #endif
207     return Status::OK;
208 }
209 } // namespace Media
210 } // namespace OHOS
211