1/*
2 * Copyright (c) 2021 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 "buffer_utils.h"
17
18#include <fcntl.h>
19#include <unistd.h>
20
21#include "buffer_log.h"
22#include "surface_buffer_impl.h"
23
24#include <securec.h>
25#include <thread>
26#include <fstream>
27#include <sstream>
28#include <sys/time.h>
29
30namespace OHOS {
31namespace {
32constexpr size_t BLOCK_SIZE = 1024 * 1024; // 1 MB block size
33}
34void ReadFileDescriptor(MessageParcel &parcel, int32_t &fd)
35{
36    fd = parcel.ReadInt32();
37    if (fd < 0) {
38        return;
39    }
40
41    fd = parcel.ReadFileDescriptor();
42}
43
44GSError WriteFileDescriptor(MessageParcel &parcel, int32_t fd)
45{
46    if (fd >= 0 && fcntl(fd, F_GETFL) == -1 && errno == EBADF) {
47        fd = -1;
48    }
49
50    if (!parcel.WriteInt32(fd)) {
51        return GSERROR_BINDER;
52    }
53
54    if (fd < 0) {
55        return GSERROR_OK;
56    }
57
58    if (!parcel.WriteFileDescriptor(fd)) {
59        return GSERROR_BINDER;
60    }
61    return GSERROR_OK;
62}
63
64void ReadRequestConfig(MessageParcel &parcel, BufferRequestConfig &config)
65{
66    config.width = parcel.ReadInt32();
67    config.height = parcel.ReadInt32();
68    config.strideAlignment = parcel.ReadInt32();
69    config.format = parcel.ReadInt32();
70    config.usage = parcel.ReadUint64();
71    config.timeout = parcel.ReadInt32();
72    config.colorGamut = static_cast<GraphicColorGamut>(parcel.ReadInt32());
73    if (config.colorGamut < GRAPHIC_COLOR_GAMUT_INVALID || config.colorGamut > GRAPHIC_COLOR_GAMUT_DISPLAY_BT2020) {
74        config.colorGamut = GRAPHIC_COLOR_GAMUT_INVALID;
75    }
76    config.transform = static_cast<GraphicTransformType>(parcel.ReadInt32());
77    if (config.transform < GRAPHIC_ROTATE_NONE || config.transform > GRAPHIC_ROTATE_BUTT) {
78        config.transform = GRAPHIC_ROTATE_BUTT;
79    }
80}
81
82GSError WriteRequestConfig(MessageParcel &parcel, BufferRequestConfig const & config)
83{
84    if (!parcel.WriteInt32(config.width) || !parcel.WriteInt32(config.height) ||
85        !parcel.WriteInt32(config.strideAlignment) || !parcel.WriteInt32(config.format) ||
86        !parcel.WriteUint64(config.usage) || !parcel.WriteInt32(config.timeout) ||
87        !parcel.WriteInt32(static_cast<int32_t>(config.colorGamut)) ||
88        !parcel.WriteInt32(static_cast<int32_t>(config.transform))) {
89        return GSERROR_BINDER;
90    }
91    return GSERROR_OK;
92}
93
94GSError ReadFlushConfig(MessageParcel &parcel, BufferFlushConfigWithDamages &config)
95{
96    uint32_t size = parcel.ReadUint32();
97    if (size == 0) {
98        BLOGE("ReadFlushConfig size is 0");
99        return GSERROR_BINDER;
100    }
101    if (size > SURFACE_PARCEL_SIZE_LIMIT) {
102        BLOGE("ReadFlushConfig size more than limit, size: %{public}u", size);
103        return GSERROR_BINDER;
104    }
105    config.damages.clear();
106    config.damages.reserve(size);
107    for (uint32_t i = 0; i < size; i++) {
108        Rect rect = {
109            .x = parcel.ReadInt32(),
110            .y = parcel.ReadInt32(),
111            .w = parcel.ReadInt32(),
112            .h = parcel.ReadInt32(),
113        };
114        config.damages.emplace_back(rect);
115    }
116    config.timestamp = parcel.ReadInt64();
117    config.desiredPresentTimestamp = parcel.ReadInt64();
118    return GSERROR_OK;
119}
120
121GSError WriteFlushConfig(MessageParcel &parcel, BufferFlushConfigWithDamages const & config)
122{
123    uint32_t size = config.damages.size();
124    if (size > SURFACE_PARCEL_SIZE_LIMIT) {
125        BLOGE("WriteFlushConfig size more than limit, size: %{public}u", size);
126        return GSERROR_INVALID_ARGUMENTS;
127    }
128    if (!parcel.WriteUint32(size)) {
129        return GSERROR_BINDER;
130    }
131    for (const auto& rect : config.damages) {
132        if (!parcel.WriteInt32(rect.x) || !parcel.WriteInt32(rect.y) ||
133            !parcel.WriteInt32(rect.w) || !parcel.WriteInt32(rect.h)) {
134            return GSERROR_BINDER;
135        }
136    }
137    if (!parcel.WriteInt64(config.timestamp)) {
138        return GSERROR_BINDER;
139    }
140
141    if (!parcel.WriteInt64(config.desiredPresentTimestamp)) {
142        return GSERROR_BINDER;
143    }
144    return GSERROR_OK;
145}
146
147GSError ReadSurfaceBufferImpl(MessageParcel &parcel,
148                              uint32_t &sequence, sptr<SurfaceBuffer>& buffer)
149{
150    GSError ret = GSERROR_OK;
151    sequence = parcel.ReadUint32();
152    if (parcel.ReadBool()) {
153        buffer = new SurfaceBufferImpl(sequence);
154        ret = buffer->ReadFromMessageParcel(parcel);
155    }
156    return ret;
157}
158
159GSError WriteSurfaceBufferImpl(MessageParcel &parcel,
160    uint32_t sequence, const sptr<SurfaceBuffer> &buffer)
161{
162    if (!parcel.WriteUint32(sequence)) {
163        return GSERROR_BINDER;
164    }
165    if (!parcel.WriteBool(buffer != nullptr)) {
166        return GSERROR_BINDER;
167    }
168    if (buffer != nullptr) {
169        return buffer->WriteToMessageParcel(parcel);
170    }
171    return GSERROR_OK;
172}
173
174void ReadVerifyAllocInfo(MessageParcel &parcel, std::vector<BufferVerifyAllocInfo> &infos)
175{
176    uint32_t size = parcel.ReadUint32();
177    if (size > SURFACE_PARCEL_SIZE_LIMIT) {
178        BLOGE("ReadVerifyAllocInfo size more than limit, size: %{public}u", size);
179        return;
180    }
181    infos.clear();
182    BufferVerifyAllocInfo info;
183    for (uint32_t index = 0; index < size; index++) {
184        info.width = parcel.ReadUint32();
185        info.height = parcel.ReadUint32();
186        info.usage = parcel.ReadUint64();
187        info.format = static_cast<GraphicPixelFormat>(parcel.ReadInt32());
188        infos.emplace_back(info);
189    }
190}
191
192GSError WriteVerifyAllocInfo(MessageParcel &parcel, const std::vector<BufferVerifyAllocInfo> &infos)
193{
194    uint32_t size = infos.size();
195    if (size > SURFACE_PARCEL_SIZE_LIMIT) {
196        BLOGE("WriteVerifyAllocInfo size more than limit, size: %{public}u", size);
197        return GSERROR_INVALID_ARGUMENTS;
198    }
199    if (!parcel.WriteUint32(size)) {
200        return GSERROR_BINDER;
201    }
202    for (const auto &info : infos) {
203        if (!parcel.WriteUint32(info.width) || !parcel.WriteUint32(info.height) ||
204            !parcel.WriteUint64(info.usage) || !parcel.WriteInt32(info.format)) {
205            return GSERROR_BINDER;
206        }
207    }
208    return GSERROR_OK;
209}
210
211GSError ReadHDRMetaData(MessageParcel &parcel, std::vector<GraphicHDRMetaData> &metaData)
212{
213    uint32_t size = parcel.ReadUint32();
214    if (size > SURFACE_PARCEL_SIZE_LIMIT) {
215        BLOGE("ReadHDRMetaData size more than limit, size: %{public}u", size);
216        return GSERROR_BINDER;
217    }
218    metaData.clear();
219    GraphicHDRMetaData data;
220    for (uint32_t index = 0; index < size; index++) {
221        data.key = static_cast<GraphicHDRMetadataKey>(parcel.ReadUint32());
222        data.value = parcel.ReadFloat();
223        metaData.emplace_back(data);
224    }
225    return GSERROR_OK;
226}
227
228GSError WriteHDRMetaData(MessageParcel &parcel, const std::vector<GraphicHDRMetaData> &metaData)
229{
230    uint32_t size = metaData.size();
231    if (size > SURFACE_PARCEL_SIZE_LIMIT) {
232        BLOGE("WriteHDRMetaData size more than limit, size: %{public}u", size);
233        return GSERROR_INVALID_ARGUMENTS;
234    }
235    if (!parcel.WriteUint32(size)) {
236        return GSERROR_BINDER;
237    }
238    for (const auto &data : metaData) {
239        if (!parcel.WriteUint32(static_cast<uint32_t>(data.key)) || !parcel.WriteFloat(data.value)) {
240            return GSERROR_BINDER;
241        }
242    }
243    return GSERROR_OK;
244}
245
246GSError ReadHDRMetaDataSet(MessageParcel &parcel, std::vector<uint8_t> &metaData)
247{
248    uint32_t size = parcel.ReadUint32();
249    if (size > SURFACE_PARCEL_SIZE_LIMIT) {
250        BLOGE("ReadHDRMetaDataSet size more than limit, size: %{public}u", size);
251        return GSERROR_BINDER;
252    }
253    metaData.clear();
254    for (uint32_t index = 0; index < size; index++) {
255        uint8_t data = parcel.ReadUint8();
256        metaData.emplace_back(data);
257    }
258    return GSERROR_OK;
259}
260
261GSError WriteHDRMetaDataSet(MessageParcel &parcel, const std::vector<uint8_t> &metaData)
262{
263    uint32_t size = metaData.size();
264    if (size > SURFACE_PARCEL_SIZE_LIMIT) {
265        BLOGE("WriteHDRMetaDataSet size more than limit, size: %{public}u", size);
266        return GSERROR_INVALID_ARGUMENTS;
267    }
268    if (!parcel.WriteUint32(size)) {
269        return GSERROR_BINDER;
270    }
271    for (const auto &data : metaData) {
272        if (!parcel.WriteUint8(data)) {
273            return GSERROR_BINDER;
274        }
275    }
276    return GSERROR_OK;
277}
278
279GSError ReadExtDataHandle(MessageParcel &parcel, sptr<SurfaceTunnelHandle> &handle)
280{
281    if (handle == nullptr) {
282        BLOGE("handle is null");
283        return GSERROR_BINDER;
284    }
285    uint32_t reserveInts = parcel.ReadUint32();
286    if (reserveInts > SURFACE_PARCEL_SIZE_LIMIT) {
287        BLOGE("ReadExtDataHandle size more than limit, size: %{public}u", reserveInts);
288        return GSERROR_BINDER;
289    }
290    GraphicExtDataHandle *tunnelHandle = AllocExtDataHandle(reserveInts);
291    if (tunnelHandle == nullptr) {
292        BLOGE("AllocExtDataHandle failed");
293        return GSERROR_BINDER;
294    }
295    ReadFileDescriptor(parcel, tunnelHandle->fd);
296    for (uint32_t index = 0; index < reserveInts; index++) {
297        tunnelHandle->reserve[index] = parcel.ReadInt32();
298    }
299    if (handle->SetHandle(tunnelHandle) != GSERROR_OK) {
300        BLOGE("SetHandle failed");
301        FreeExtDataHandle(tunnelHandle);
302        return GSERROR_BINDER;
303    }
304    FreeExtDataHandle(tunnelHandle);
305    return GSERROR_OK;
306}
307
308GSError WriteExtDataHandle(MessageParcel &parcel, const GraphicExtDataHandle *handle)
309{
310    if (handle == nullptr) {
311        BLOGE("handle is null");
312        return GSERROR_INVALID_ARGUMENTS;
313    }
314    uint32_t reserveInts = handle->reserveInts;
315    if (reserveInts > SURFACE_PARCEL_SIZE_LIMIT) {
316        BLOGE("WriteExtDataHandle size more than limit, size: %{public}u", reserveInts);
317        return GSERROR_INVALID_ARGUMENTS;
318    }
319    if (!parcel.WriteUint32(reserveInts)) {
320        return GSERROR_BINDER;
321    }
322    GSError ret = WriteFileDescriptor(parcel, handle->fd);
323    if (ret != GSERROR_OK) {
324        return ret;
325    }
326    for (uint32_t index = 0; index < handle->reserveInts; index++) {
327        if (!parcel.WriteInt32(handle->reserve[index])) {
328            return GSERROR_BINDER;
329        }
330    }
331    return GSERROR_OK;
332}
333
334void CloneBuffer(uint8_t* dest, const uint8_t* src, size_t totalSize)
335{
336    if (dest == nullptr || src == nullptr) {
337        return;
338    }
339    size_t num_blocks = totalSize / BLOCK_SIZE;
340    size_t last_block_size = totalSize % BLOCK_SIZE;
341
342    // Obtain the number of parallelizable threads.
343    size_t num_threads = std::thread::hardware_concurrency();
344    num_threads = num_threads > 0 ? num_threads : 1;
345
346    size_t blocks_per_thread = num_blocks / num_threads;
347    size_t remaining_blocks = num_blocks % num_threads;
348
349    // Lambda function to copy a block of memory
350    auto copy_block = [&](uint8_t* current_dest, const uint8_t* current_src, size_t size) {
351        auto ret = memcpy_s(current_dest, size, current_src, size);
352        if (ret != EOK) {
353            BLOGE("memcpy_s ret:%{public}d", static_cast<int>(ret));
354        }
355    };
356
357    // Vector to store threads
358    std::vector<std::thread> threads;
359    uint8_t* current_dest = dest;
360    const uint8_t* current_src = src;
361
362    // Create threads and copy blocks of memory
363    for (size_t i = 0; i < num_threads; ++i) {
364        size_t blocks_to_copy = blocks_per_thread + (i < remaining_blocks ? 1 : 0);
365        size_t length_to_copy = blocks_to_copy * BLOCK_SIZE;
366
367        threads.emplace_back(copy_block, current_dest, current_src, length_to_copy);
368
369        current_dest += length_to_copy;
370        current_src += length_to_copy;
371    }
372
373    if (last_block_size > 0) {
374        threads.emplace_back(copy_block, current_dest, current_src, last_block_size);
375    }
376
377    // Wait for all threads to finish
378    for (auto& th : threads) {
379        if (th.joinable()) {
380            th.join();
381        }
382    }
383}
384
385void WriteToFile(std::string prefixPath, std::string pid, void* dest, size_t size, int32_t format, int32_t width,
386    int32_t height, const std::string name)
387{
388    if (dest == nullptr) {
389        BLOGE("dest is nulltr");
390        return;
391    }
392    struct timeval now;
393    gettimeofday(&now, nullptr);
394    constexpr int secToUsec = 1000 * 1000;
395    int64_t nowVal = (int64_t)now.tv_sec * secToUsec + (int64_t)now.tv_usec;
396
397    std::stringstream ss;
398    ss << prefixPath << pid << "_" << name << "_" << nowVal << "_" << format << "_"
399        << width << "x" << height << ".raw";
400
401    // Open the file for writing in binary mode
402    std::ofstream rawDataFile(ss.str(), std::ofstream::binary);
403    if (!rawDataFile.good()) {
404        BLOGE("open failed: (%{public}d)%{public}s", errno, strerror(errno));
405        free(dest);
406        return;
407    }
408
409    // Write the data to the file
410    rawDataFile.write(static_cast<const char *>(dest), size);
411    rawDataFile.flush();
412    rawDataFile.close();
413
414    // Free the memory allocated for the data
415    free(dest);
416}
417
418GSError DumpToFileAsync(pid_t pid, std::string name, sptr<SurfaceBuffer> &buffer)
419{
420    bool rsDumpFlag = access("/data/bq_dump", F_OK) == 0;
421    bool appDumpFlag = access("/data/storage/el1/base/bq_dump", F_OK) == 0;
422    if (!rsDumpFlag && !appDumpFlag) {
423        return GSERROR_OK;
424    }
425
426    if (buffer == nullptr) {
427        BLOGE("buffer is a nullptr.");
428        return GSERROR_INVALID_ARGUMENTS;
429    }
430
431    size_t size = buffer->GetSize();
432    if (size > 0) {
433        uint8_t* src = static_cast<uint8_t*>(buffer->GetVirAddr());
434
435        if (src == nullptr) {
436            BLOGE("src is a nullptr.");
437            return GSERROR_INVALID_ARGUMENTS;
438        }
439
440        uint8_t* dest = static_cast<uint8_t*>(malloc(size));
441        if (dest != nullptr) {
442            // Copy through multithreading
443            CloneBuffer(dest, src, size);
444
445            std::string prefixPath = "/data/bq_";
446            if (appDumpFlag) {
447                // Is app texture export
448                prefixPath = "/data/storage/el1/base/bq_";
449            }
450
451            // create dump thread,async export file
452            std::thread file_writer(WriteToFile, prefixPath, std::to_string(pid), dest, size, buffer->GetFormat(),
453                buffer->GetWidth(), buffer->GetHeight(), name);
454            file_writer.detach();
455        } else {
456            BLOGE("dest is a nullptr.");
457            return GSERROR_INTERNAL;
458        }
459    } else {
460        BLOGE("BufferDump buffer size(%{public}zu) error.", size);
461        return GSERROR_INTERNAL;
462    }
463
464    return GSERROR_OK;
465}
466} // namespace OHOS
467