1 /*
2  * Copyright (C) 2024 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 <message_parcel.h>
17 #include <securec.h>
18 #include <surface_buffer.h>
19 #include <sys/mman.h>
20 
21 #include "media_errors.h"
22 #include "pixel_map.h"
23 #include "rs_profiler.h"
24 #include "rs_profiler_cache.h"
25 #include "rs_profiler_utils.h"
26 
27 #include "transaction/rs_marshalling_helper.h"
28 
29 namespace OHOS::Media {
30 
IncrementSurfaceBufferReference(sptr<SurfaceBuffer> & buffer)31 SurfaceBuffer* IncrementSurfaceBufferReference(sptr<SurfaceBuffer>& buffer)
32 {
33     if (auto object = buffer.GetRefPtr()) {
34         OHOS::RefBase *ref = reinterpret_cast<OHOS::RefBase *>(object);
35         ref->IncStrongRef(ref);
36         return object;
37     }
38     return nullptr;
39 }
40 
IsDataValid(const void * data,size_t size)41 static bool IsDataValid(const void* data, size_t size)
42 {
43     return data && (size > 0);
44 }
45 
GenerateRawCopy(const uint8_t * data,size_t size)46 static std::vector<uint8_t> GenerateRawCopy(const uint8_t* data, size_t size)
47 {
48     std::vector<uint8_t> out;
49     if (IsDataValid(data, size)) {
50         out.insert(out.end(), data, data + size);
51     }
52     return out;
53 }
54 
GenerateMiniatureAstc(const uint8_t * data,size_t size)55 static std::vector<uint8_t> GenerateMiniatureAstc(const uint8_t* data, size_t size)
56 {
57     constexpr uint32_t astcBytesPerPixel = 16u;
58     return GenerateRawCopy(data, astcBytesPerPixel);
59 }
60 
GenerateMiniature(const uint8_t * data,size_t size,uint32_t pixelBytes)61 static std::vector<uint8_t> GenerateMiniature(const uint8_t* data, size_t size, uint32_t pixelBytes)
62 {
63     if (!IsDataValid(data, size)) {
64         return {};
65     }
66 
67     constexpr uint32_t rgbaBytesPerPixel = 4u;
68     constexpr uint32_t pixelBytesThreshold = 256u; // in case the pixelBytes field in the map has invalid value
69     const uint32_t bytesPerPixel =
70         ((pixelBytes > 0) && (pixelBytes < pixelBytesThreshold)) ? pixelBytes : rgbaBytesPerPixel;
71 
72     const auto pixelCount = size / bytesPerPixel;
73 
74     std::vector<uint64_t> averageValue(bytesPerPixel, 0);
75     constexpr uint32_t sampleCount = 100u;
76     for (uint32_t sample = 0; sample < sampleCount; sample++) {
77         for (uint32_t channel = 0; channel < bytesPerPixel; channel++) {
78             averageValue[channel] += data[(sample * pixelCount / sampleCount) * bytesPerPixel + channel];
79         }
80     }
81 
82     std::vector<uint8_t> out(bytesPerPixel, 0);
83     for (uint32_t i = 0; i < bytesPerPixel; i++) {
84         out[i] = static_cast<uint8_t>(averageValue[i] / sampleCount);
85     }
86     return out;
87 }
88 
GenerateImageData(const uint8_t * data,size_t size,bool isAstc,uint32_t pixelBytes)89 static std::vector<uint8_t> GenerateImageData(const uint8_t* data, size_t size, bool isAstc, uint32_t pixelBytes)
90 {
91     if (!Rosen::RSProfiler::IsBetaRecordEnabled()) {
92         return GenerateRawCopy(data, size);
93     }
94 
95     return isAstc ? GenerateMiniatureAstc(data, size) : GenerateMiniature(data, size, pixelBytes);
96 }
97 
GenerateImageData(const uint8_t * data,size_t size,PixelMap & map)98 static std::vector<uint8_t> GenerateImageData(const uint8_t* data, size_t size, PixelMap& map)
99 {
100     return GenerateImageData(data, size, map.IsAstc(), map.GetPixelBytes());
101 }
102 
CopyImageData(const uint8_t * srcImage,size_t srcSize,uint8_t * dstImage,size_t dstSize)103 static bool CopyImageData(const uint8_t* srcImage, size_t srcSize, uint8_t* dstImage, size_t dstSize)
104 {
105     if (!srcImage || !dstImage || (srcSize == 0) || (dstSize == 0) || (srcSize > dstSize)) {
106         return false;
107     }
108 
109     if (dstSize == srcSize) {
110         return Rosen::Utils::Move(dstImage, dstSize, srcImage, srcSize);
111     }
112 
113     for (size_t offset = 0; offset < dstSize;) {
114         const size_t size = std::min(dstSize - offset, srcSize);
115         if (!Rosen::Utils::Move(dstImage + offset, size, srcImage, size)) {
116             return false;
117         }
118         offset += size;
119     }
120 
121     return true;
122 }
123 
CopyImageData(const std::vector<uint8_t> & data,uint8_t * dstImage,size_t dstSize)124 static bool CopyImageData(const std::vector<uint8_t>& data, uint8_t* dstImage, size_t dstSize)
125 {
126     return CopyImageData(data.data(), data.size(), dstImage, dstSize);
127 }
128 
CopyImageData(const Rosen::Image * image,uint8_t * dstImage,size_t dstSize)129 static bool CopyImageData(const Rosen::Image* image, uint8_t* dstImage, size_t dstSize)
130 {
131     return image ? CopyImageData(image->data, dstImage, dstSize) : false;
132 }
133 
134 struct UnmarshallingContext {
135 public:
136     static constexpr int headerLength = 24; // NOLINT
137 
138 public:
UnmarshallingContextOHOS::Media::UnmarshallingContext139     explicit UnmarshallingContext(Parcel& parcel) : parcel(parcel) {}
140 
GatherImageFromFileOHOS::Media::UnmarshallingContext141     bool GatherImageFromFile(const Rosen::Image* image)
142     {
143         if ((size <= 0) || (size > Rosen::Image::maxSize)) {
144             return false;
145         }
146 
147         base = static_cast<uint8_t*>(malloc(size));
148         if (!base) {
149             return false;
150         }
151 
152         if (!CopyImageData(image, base, size)) {
153             delete base;
154             base = nullptr;
155             return false;
156         }
157 
158         context = nullptr;
159         return true;
160     }
161 
GatherDmaImageFromFileOHOS::Media::UnmarshallingContext162     bool GatherDmaImageFromFile(const Rosen::Image* image)
163     {
164         if ((size <= 0) || (size > Rosen::Image::maxSize)) {
165             return false;
166         }
167 
168         sptr<SurfaceBuffer> surfaceBuffer = SurfaceBuffer::Create();
169         if (!surfaceBuffer) {
170             return false;
171         }
172 
173         const BufferRequestConfig config = { .width = image->dmaWidth,
174             .height = image->dmaHeight,
175             .strideAlignment = image->dmaStride,
176             .format = image->dmaFormat,
177             .usage = image->dmaUsage };
178         surfaceBuffer->Alloc(config);
179 
180         base = static_cast<uint8_t*>(surfaceBuffer->GetVirAddr());
181         if (base && CopyImageData(image, base, image->dmaSize)) {
182             context = IncrementSurfaceBufferReference(surfaceBuffer);
183             return true;
184         }
185         return false;
186     }
187 
188 public:
189     Parcel& parcel;
190     std::unique_ptr<PixelMap> map = std::make_unique<Media::PixelMap>();
191     ImageInfo info;
192     AllocatorType allocType = AllocatorType::DEFAULT;
193     size_t rowPitch = 0;
194     size_t size = 0;
195     uint8_t* base = nullptr;
196     void* context = nullptr;
197 };
198 
199 // This class has to be 'reimplemented' here to get access to the PixelMap's private functions.
200 // It works ONLY thanks to the 'friend class ImageSource' in PixelMap.
201 class ImageSource {
202 public:
203     static PixelMap* Unmarshal(Parcel& parcel);
204     static bool Marshal(Parcel& parcel, PixelMap& map);
205 
206 private:
207     static void CacheImage(
208         uint64_t id, const std::vector<uint8_t>& data, size_t skipBytes, BufferHandle* bufferHandle = nullptr);
209     static Rosen::Image* GetCachedImage(uint64_t id);
210 
211     static uint8_t* MapImage(int32_t file, size_t size, int32_t flags);
212     static void UnmapImage(void* image, size_t size);
213 
214     static void OnClientMarshalling(PixelMap& map, uint64_t id);
215 
216     static bool InitUnmarshalling(UnmarshallingContext& context);
217     static bool UnmarshalFromSharedMemory(UnmarshallingContext& context, uint64_t id);
218     static bool UnmarshalFromDMA(UnmarshallingContext& context, uint64_t id);
219     static bool UnmarshalFromData(UnmarshallingContext& context);
220     static PixelMap* FinalizeUnmarshalling(UnmarshallingContext& context);
221 };
222 
CacheImage(uint64_t id,const std::vector<uint8_t> & data,size_t skipBytes,BufferHandle * bufferHandle)223 void ImageSource::CacheImage(
224     uint64_t id, const std::vector<uint8_t>& data, size_t skipBytes, BufferHandle* bufferHandle)
225 {
226     if (data.empty()) {
227         return;
228     }
229 
230     if (bufferHandle && ((bufferHandle->width == 0) || (bufferHandle->height == 0))) {
231         return;
232     }
233 
234     Rosen::Image image;
235     image.data = data;
236 
237     if (bufferHandle) {
238         image.dmaSize = static_cast<size_t>(bufferHandle->size);
239         image.dmaWidth = bufferHandle->width;
240         image.dmaHeight = bufferHandle->height;
241         image.dmaStride = bufferHandle->stride;
242         image.dmaFormat = bufferHandle->format;
243         image.dmaUsage = bufferHandle->usage;
244     }
245 
246     image.parcelSkipBytes = skipBytes;
247     Rosen::ImageCache::Add(id, std::move(image));
248 }
249 
GetCachedImage(uint64_t id)250 Rosen::Image* ImageSource::GetCachedImage(uint64_t id)
251 {
252     return Rosen::ImageCache::Get(id);
253 }
254 
MapImage(int32_t file,size_t size,int32_t flags)255 uint8_t* ImageSource::MapImage(int32_t file, size_t size, int32_t flags)
256 {
257     auto image = ::mmap(nullptr, size, flags, MAP_SHARED, file, 0);
258     return (image != MAP_FAILED) ? reinterpret_cast<uint8_t*>(image) : nullptr; // NOLINT
259 }
260 
UnmapImage(void * image,size_t size)261 void ImageSource::UnmapImage(void* image, size_t size)
262 {
263     if (IsDataValid(image, size)) {
264         ::munmap(image, size);
265     }
266 }
267 
InitUnmarshalling(UnmarshallingContext & context)268 bool ImageSource::InitUnmarshalling(UnmarshallingContext& context)
269 {
270     if (!context.map) {
271         return false;
272     }
273 
274     if (!context.map->ReadImageInfo(context.parcel, context.info)) {
275         return false;
276     }
277 
278     context.map->SetEditable(context.parcel.ReadBool());
279 
280     const bool isAstc = context.parcel.ReadBool();
281     context.map->SetAstc(isAstc);
282 
283     context.allocType = static_cast<AllocatorType>(context.parcel.ReadInt32());
284     context.parcel.ReadInt32(); // unused csm
285     context.rowPitch = static_cast<size_t>(context.parcel.ReadInt32());
286     context.size = static_cast<size_t>(context.parcel.ReadInt32());
287 
288     const size_t rawSize = static_cast<size_t>(context.rowPitch * context.info.size.height);
289     return (isAstc || (context.size == rawSize));
290 }
291 
UnmarshalFromSharedMemory(UnmarshallingContext & context,uint64_t id)292 bool ImageSource::UnmarshalFromSharedMemory(UnmarshallingContext& context, uint64_t id)
293 {
294     constexpr int32_t invalidFile = -1;
295     const int32_t file =
296         Rosen::RSProfiler::IsParcelMock(context.parcel) ? invalidFile : context.map->ReadFileDescriptor(context.parcel);
297     if (file < 0) {
298         if (auto image = GetCachedImage(id)) {
299             if (context.GatherImageFromFile(image)) {
300                 context.parcel.SkipBytes(UnmarshallingContext::headerLength);
301                 return true;
302             }
303         }
304         return false;
305     }
306 
307     auto image = MapImage(file, context.size, PROT_READ | PROT_WRITE);
308     if (!image) {
309         image = MapImage(file, context.size, PROT_READ);
310         if (!image) {
311             ::close(file);
312             return false;
313         }
314     }
315 
316     const auto imageData = GenerateImageData(image, context.size, *context.map);
317     CacheImage(id, imageData, UnmarshallingContext::headerLength);
318 
319     context.context = new int32_t();
320     if (!context.context) {
321         UnmapImage(image, context.size);
322         ::close(file);
323         return false;
324     }
325     *static_cast<int32_t*>(context.context) = file;
326     context.base = image;
327     return true;
328 }
329 
UnmarshalFromDMA(UnmarshallingContext & context,uint64_t id)330 bool ImageSource::UnmarshalFromDMA(UnmarshallingContext& context, uint64_t id)
331 {
332     auto image = Rosen::RSProfiler::IsParcelMock(context.parcel) ? GetCachedImage(id) : nullptr;
333     if (image) {
334         context.parcel.SkipBytes(image->parcelSkipBytes);
335         return context.GatherDmaImageFromFile(image);
336     }
337 
338     const size_t readPosition = context.parcel.GetReadPosition();
339 
340     sptr<SurfaceBuffer> surfaceBuffer = SurfaceBuffer::Create();
341     surfaceBuffer->ReadFromMessageParcel(static_cast<MessageParcel&>(context.parcel));
342     context.base = static_cast<uint8_t*>(surfaceBuffer->GetVirAddr());
343     context.context = IncrementSurfaceBufferReference(surfaceBuffer);
344 
345     if (auto bufferHandle = surfaceBuffer->GetBufferHandle()) {
346         const auto imageData = GenerateImageData(context.base, bufferHandle->size, *context.map);
347         CacheImage(id, imageData, context.parcel.GetReadPosition() - readPosition, bufferHandle);
348     }
349 
350     return true;
351 }
352 
UnmarshalFromData(UnmarshallingContext & context)353 bool ImageSource::UnmarshalFromData(UnmarshallingContext& context)
354 {
355     context.base = context.map->ReadImageData(context.parcel, context.size);
356     return (context.base != nullptr);
357 }
358 
FinalizeUnmarshalling(UnmarshallingContext & context)359 PixelMap* ImageSource::FinalizeUnmarshalling(UnmarshallingContext& context)
360 {
361     if (context.map->SetImageInfo(context.info) != SUCCESS) {
362         if (context.map->freePixelMapProc_) {
363             context.map->freePixelMapProc_(context.base, context.context, context.size);
364         }
365         context.map->ReleaseMemory(context.allocType, context.base, context.context, context.size);
366         if (context.context && (context.allocType == AllocatorType::SHARE_MEM_ALLOC)) {
367             delete static_cast<int32_t*>(context.context);
368         }
369 
370         return nullptr;
371     }
372     context.map->SetPixelsAddr(context.base, context.context, context.size, context.allocType, nullptr);
373     if (!context.map->ReadTransformData(context.parcel, context.map.get())) {
374         return nullptr;
375     }
376     if (!context.map->ReadAstcRealSize(context.parcel, context.map.get())) {
377         return nullptr;
378     }
379 
380     return context.map.release();
381 }
382 
Unmarshal(Parcel & parcel)383 PixelMap* ImageSource::Unmarshal(Parcel& parcel)
384 {
385     const uint64_t id = parcel.ReadUint64();
386     UnmarshallingContext context { parcel };
387 
388     if (!InitUnmarshalling(context)) {
389         return nullptr;
390     }
391 
392 #if !defined(_WIN32) && !defined(_APPLE) && !defined(IOS_PLATFORM) && !defined(A_PLATFORM)
393     if (context.allocType == AllocatorType::SHARE_MEM_ALLOC) {
394         if (!UnmarshalFromSharedMemory(context, id)) {
395             return nullptr;
396         }
397     } else if (context.allocType == AllocatorType::DMA_ALLOC) {
398         if (!UnmarshalFromDMA(context, id)) {
399             return nullptr;
400         }
401     } else {
402         if (!UnmarshalFromData(context)) {
403             return nullptr;
404         }
405     }
406 #else
407     if (!UnmarshalFromData(context)) {
408         return nullptr;
409     }
410 #endif
411 
412     return FinalizeUnmarshalling(context);
413 }
414 
OnClientMarshalling(Media::PixelMap & map,uint64_t id)415 void ImageSource::OnClientMarshalling(Media::PixelMap& map, uint64_t id)
416 {
417     if (Rosen::RSProfiler::IsSharedMemoryEnabled()) {
418         return;
419     }
420 
421     const auto descriptor = map.GetFd();
422     if (!descriptor) {
423         return;
424     }
425 
426     if (map.GetAllocatorType() == AllocatorType::DMA_ALLOC) {
427         auto surfaceBuffer = reinterpret_cast<SurfaceBuffer*>(descriptor);
428         if (auto bufferHandle = surfaceBuffer->GetBufferHandle()) {
429             const auto imageData = GenerateImageData(
430                 reinterpret_cast<const uint8_t*>(surfaceBuffer->GetVirAddr()), bufferHandle->size, map);
431             MessageParcel parcel2;
432             surfaceBuffer->WriteToMessageParcel(parcel2);
433             size_t bufferHandleSize = parcel2.GetReadableBytes();
434             CacheImage(id, imageData, bufferHandleSize, bufferHandle);
435         }
436     } else {
437         const size_t size = map.isAstc_ ? map.pixelsSize_ :
438             static_cast<size_t>(map.rowDataSize_ * map.imageInfo_.size.height);
439         if (auto image = MapImage(*reinterpret_cast<const int32_t*>(map.GetFd()), size, PROT_READ)) {
440             const auto imageData = GenerateImageData(image, size, map);
441             CacheImage(id, imageData, UnmarshallingContext::headerLength);
442             UnmapImage(image, size);
443         }
444     }
445 }
446 
Marshal(Parcel & parcel,Media::PixelMap & map)447 bool ImageSource::Marshal(Parcel& parcel, Media::PixelMap& map)
448 {
449     const uint64_t id = Rosen::ImageCache::New();
450     if (!parcel.WriteUint64(id) || !map.Marshalling(parcel)) {
451         return false;
452     }
453     OnClientMarshalling(map, id);
454     return true;
455 }
456 
457 } // namespace OHOS::Media
458 
459 namespace OHOS::Rosen {
460 
461 using PixelMapHelper = Media::ImageSource;
462 
UnmarshalPixelMap(Parcel & parcel)463 Media::PixelMap* RSProfiler::UnmarshalPixelMap(Parcel& parcel)
464 {
465     if (!IsEnabled()) {
466         return Media::PixelMap::Unmarshalling(parcel);
467     }
468 
469     return PixelMapHelper::Unmarshal(parcel);
470 }
471 
MarshalPixelMap(Parcel & parcel,const std::shared_ptr<Media::PixelMap> & map)472 bool RSProfiler::MarshalPixelMap(Parcel& parcel, const std::shared_ptr<Media::PixelMap>& map)
473 {
474     if (!map) {
475         return false;
476     }
477 
478     if (!IsEnabled()) {
479         return map->Marshalling(parcel);
480     }
481 
482     return PixelMapHelper::Marshal(parcel, *map);
483 }
484 
485 } // namespace OHOS::Rosen